// 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; using System.Collections.Generic; using System.Data.Linq; using System.Data.Objects.DataClasses; using System.Drawing; using System.Globalization; using System.IO; using System.Web.UI.WebControls; using System.Web.WebPages.Scope; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; using Moq; namespace System.Web.Mvc.Html.Test { [Xunit.Collection("Uses ScopeStorage or ViewEngines.Engines")] public class DefaultEditorTemplatesTest : IDisposable { // BooleanTemplate [Fact] public void BooleanTemplateTests() { // Boolean values Assert.Equal( "", DefaultEditorTemplates.BooleanTemplate(MakeHtmlHelper(true))); Assert.Equal( "", DefaultEditorTemplates.BooleanTemplate(MakeHtmlHelper(false))); Assert.Equal( "", DefaultEditorTemplates.BooleanTemplate(MakeHtmlHelper(null))); // Nullable values Assert.Equal( "", DefaultEditorTemplates.BooleanTemplate(MakeHtmlHelper>(true))); Assert.Equal( "", DefaultEditorTemplates.BooleanTemplate(MakeHtmlHelper>(false))); Assert.Equal( "", DefaultEditorTemplates.BooleanTemplate(MakeHtmlHelper>(null))); } public static TheoryDataSet BooleanTemplateHtmlAttributeData { get { return new TheoryDataSet { { new { @class = "form-control" }, "" }, { new { @class = "form-control", custom = "foo" }, "" } }; } } [Theory] [PropertyData("BooleanTemplateHtmlAttributeData")] public void BooleanTemplate_AddsHtmlAttributes(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper(true); htmlHelper.ViewContext.ViewBag.htmlAttributes = htmlAttributes; Assert.Equal( expectedHtml, DefaultEditorTemplates.BooleanTemplate(htmlHelper)); } [Theory] [PropertyData("BooleanTemplateHtmlAttributeData")] public void BooleanTemplate_AddsHtmlAttributesDictionary(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper(true); htmlHelper.ViewContext.ViewBag.htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes); Assert.Equal( expectedHtml, DefaultEditorTemplates.BooleanTemplate(htmlHelper)); } // CollectionTemplate private static string CollectionSpyCallback(HtmlHelper html, ModelMetadata metadata, string htmlFieldName, string templateName, DataBoundControlMode mode, object additionalViewData) { return String.Format(CultureInfo.InvariantCulture, Environment.NewLine + "Model = {0}, ModelType = {1}, PropertyName = {2}, HtmlFieldName = {3}, TemplateName = {4}, Mode = {5}, TemplateInfo.HtmlFieldPrefix = {6}, AdditionalViewData = {7}", metadata.Model ?? "(null)", metadata.ModelType == null ? "(null)" : metadata.ModelType.FullName, metadata.PropertyName ?? "(null)", htmlFieldName == String.Empty ? "(empty)" : htmlFieldName ?? "(null)", templateName ?? "(null)", mode, html.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix, AnonymousObject.Inspect(additionalViewData)); } [Fact] public void CollectionTemplateWithNullModel() { // Arrange HtmlHelper html = MakeHtmlHelper(null); // Act string result = DefaultEditorTemplates.CollectionTemplate(html, CollectionSpyCallback); // Assert Assert.Equal(String.Empty, result); } [Fact] public void CollectionTemplateNonEnumerableModelThrows() { // Arrange HtmlHelper html = MakeHtmlHelper(new object()); // Act & Assert Assert.Throws( () => DefaultEditorTemplates.CollectionTemplate(html, CollectionSpyCallback), "The Collection template was used with an object of type 'System.Object', which does not implement System.IEnumerable." ); } [Fact] public void CollectionTemplateWithSingleItemCollectionWithoutPrefix() { // Arrange HtmlHelper html = MakeHtmlHelper>(new List { "foo" }); html.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix = null; // Act string result = DefaultEditorTemplates.CollectionTemplate(html, CollectionSpyCallback); // Assert Assert.Equal( Environment.NewLine + "Model = foo, ModelType = System.String, PropertyName = (null), HtmlFieldName = [0], TemplateName = (null), Mode = Edit, TemplateInfo.HtmlFieldPrefix = , AdditionalViewData = (null)", result); } [Fact] public void CollectionTemplateWithSingleItemCollectionWithPrefix() { // Arrange HtmlHelper html = MakeHtmlHelper>(new List { "foo" }); html.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix = "ModelProperty"; // Act string result = DefaultEditorTemplates.CollectionTemplate(html, CollectionSpyCallback); // Assert Assert.Equal( Environment.NewLine + "Model = foo, ModelType = System.String, PropertyName = (null), HtmlFieldName = ModelProperty[0], TemplateName = (null), Mode = Edit, TemplateInfo.HtmlFieldPrefix = , AdditionalViewData = (null)", result); } [Fact] public void CollectionTemplateWithMultiItemCollection() { // Arrange HtmlHelper html = MakeHtmlHelper>(new List { "foo", "bar", "baz" }); html.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix = null; // Act string result = DefaultEditorTemplates.CollectionTemplate(html, CollectionSpyCallback); // Assert Assert.Equal( Environment.NewLine + "Model = foo, ModelType = System.String, PropertyName = (null), HtmlFieldName = [0], TemplateName = (null), Mode = Edit, TemplateInfo.HtmlFieldPrefix = , AdditionalViewData = (null)" + Environment.NewLine + "Model = bar, ModelType = System.String, PropertyName = (null), HtmlFieldName = [1], TemplateName = (null), Mode = Edit, TemplateInfo.HtmlFieldPrefix = , AdditionalViewData = (null)" + Environment.NewLine + "Model = baz, ModelType = System.String, PropertyName = (null), HtmlFieldName = [2], TemplateName = (null), Mode = Edit, TemplateInfo.HtmlFieldPrefix = , AdditionalViewData = (null)", result); } [Fact] public void CollectionTemplateNullITemInWeaklyTypedCollectionUsesModelTypeOfString() { // Arrange HtmlHelper html = MakeHtmlHelper(new ArrayList { null }); html.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix = null; // Act string result = DefaultEditorTemplates.CollectionTemplate(html, CollectionSpyCallback); // Assert Assert.Equal( Environment.NewLine + "Model = (null), ModelType = System.String, PropertyName = (null), HtmlFieldName = [0], TemplateName = (null), Mode = Edit, TemplateInfo.HtmlFieldPrefix = , AdditionalViewData = (null)", result); } [Fact] public void CollectionTemplateNullItemInStronglyTypedCollectionUsesModelTypeFromIEnumerable() { // Arrange HtmlHelper html = MakeHtmlHelper>(new List { null }); html.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix = null; // Act string result = DefaultEditorTemplates.CollectionTemplate(html, CollectionSpyCallback); // Assert Assert.Equal( Environment.NewLine + "Model = (null), ModelType = System.Web.IHttpHandler, PropertyName = (null), HtmlFieldName = [0], TemplateName = (null), Mode = Edit, TemplateInfo.HtmlFieldPrefix = , AdditionalViewData = (null)", result); } [Fact] public void CollectionTemplateUsesRealObjectTypes() { // Arrange HtmlHelper html = MakeHtmlHelper>(new List { 1, 2.3, "Hello World" }); html.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix = null; // Act string result = DefaultEditorTemplates.CollectionTemplate(html, CollectionSpyCallback); // Assert Assert.Equal( Environment.NewLine + "Model = 1, ModelType = System.Int32, PropertyName = (null), HtmlFieldName = [0], TemplateName = (null), Mode = Edit, TemplateInfo.HtmlFieldPrefix = , AdditionalViewData = (null)" + Environment.NewLine + "Model = 2.3, ModelType = System.Double, PropertyName = (null), HtmlFieldName = [1], TemplateName = (null), Mode = Edit, TemplateInfo.HtmlFieldPrefix = , AdditionalViewData = (null)" + Environment.NewLine + "Model = Hello World, ModelType = System.String, PropertyName = (null), HtmlFieldName = [2], TemplateName = (null), Mode = Edit, TemplateInfo.HtmlFieldPrefix = , AdditionalViewData = (null)", result); } [Fact] public void CollectionTemplateNullItemInCollectionOfNullableValueTypesDoesNotDiscardNullable() { // Arrange HtmlHelper html = MakeHtmlHelper>(new List { 1, null, 2 }); html.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix = null; // Act string result = DefaultEditorTemplates.CollectionTemplate(html, CollectionSpyCallback); // Assert Assert.Equal( Environment.NewLine + "Model = 1, ModelType = System.Nullable`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], PropertyName = (null), HtmlFieldName = [0], TemplateName = (null), Mode = Edit, TemplateInfo.HtmlFieldPrefix = , AdditionalViewData = (null)" + Environment.NewLine + "Model = (null), ModelType = System.Nullable`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], PropertyName = (null), HtmlFieldName = [1], TemplateName = (null), Mode = Edit, TemplateInfo.HtmlFieldPrefix = , AdditionalViewData = (null)" + Environment.NewLine + "Model = 2, ModelType = System.Nullable`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], PropertyName = (null), HtmlFieldName = [2], TemplateName = (null), Mode = Edit, TemplateInfo.HtmlFieldPrefix = , AdditionalViewData = (null)", result); } [Fact] public void CollectionTemplate_AddsHtmlAttributes() { // Arrange HtmlHelper html = MakeHtmlHelper>(new List { "foo", "bar", "baz" }); html.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix = null; html.ViewContext.ViewBag.htmlAttributes = new { @class = "form-control", foo = "bar" }; // Act string result = RunWithoutViewEngine(() => DefaultEditorTemplates.CollectionTemplate(html)); // Assert Assert.Equal( "" + "" + "", result); } // DecimalTemplate [Fact] public void DecimalTemplateTests() { Assert.Equal( String.Format( CultureInfo.CurrentCulture, "", 12.35M), DefaultEditorTemplates.DecimalTemplate(MakeHtmlHelper(12.3456M))); Assert.Equal( "", DefaultEditorTemplates.DecimalTemplate(MakeHtmlHelper(12.3456M, "Formatted Value"))); Assert.Equal( "alert('XSS!')</script>\" />", DefaultEditorTemplates.DecimalTemplate(MakeHtmlHelper(12.3456M, ""))); } public static TheoryDataSet DecimalTemplateHtmlAttributeData { get { return new TheoryDataSet { { new { @class = "form-control" }, "" }, { new { @class = "form-control", custom = "foo" }, "" } }; } } [Theory] [ReplaceCulture] [PropertyData("DecimalTemplateHtmlAttributeData")] public void DecimalTemplate_AddsHtmlAttributes(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper(12.30); htmlHelper.ViewContext.ViewBag.htmlAttributes = htmlAttributes; Assert.Equal( expectedHtml, DefaultEditorTemplates.DecimalTemplate(htmlHelper)); } [Theory] [ReplaceCulture] [PropertyData("DecimalTemplateHtmlAttributeData")] public void DecimalTemplate_AddsHtmlAttributesDictionary(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper(12.30); htmlHelper.ViewContext.ViewBag.htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes); Assert.Equal( expectedHtml, DefaultEditorTemplates.DecimalTemplate(htmlHelper)); } // HiddenInputTemplate [Fact] public void HiddenInputTemplateTests() { Assert.Equal( "Hidden Value", DefaultEditorTemplates.HiddenInputTemplate(MakeHtmlHelper("Hidden Value"))); Assert.Equal( "<script>alert('XSS!')</script>alert('XSS!')</script>\" />", DefaultEditorTemplates.HiddenInputTemplate(MakeHtmlHelper(""))); var helperWithInvisibleHtml = MakeHtmlHelper("", "Encode me!"); helperWithInvisibleHtml.ViewData.ModelMetadata.HideSurroundingHtml = true; Assert.Equal( "alert('XSS!')</script>\" />", DefaultEditorTemplates.HiddenInputTemplate(helperWithInvisibleHtml)); byte[] byteValues = { 1, 2, 3, 4, 5 }; Assert.Equal( ""AQIDBAU="", DefaultEditorTemplates.HiddenInputTemplate(MakeHtmlHelper(new Binary(byteValues)))); Assert.Equal( "System.Byte[]", DefaultEditorTemplates.HiddenInputTemplate(MakeHtmlHelper(byteValues))); } public static TheoryDataSet HiddenInputTemplateHtmlAttributeData { get { return new TheoryDataSet { { new { @class = "form-control" }, "Hidden Value" }, { new { @class = "form-control", custom = "foo" }, "Hidden Value" } }; } } [Theory] [PropertyData("HiddenInputTemplateHtmlAttributeData")] public void HiddenInputTemplate_AddsHtmlAttributes(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper("Hidden Value"); htmlHelper.ViewContext.ViewBag.htmlAttributes = htmlAttributes; Assert.Equal( expectedHtml, DefaultEditorTemplates.HiddenInputTemplate(htmlHelper)); } [Theory] [PropertyData("HiddenInputTemplateHtmlAttributeData")] public void HiddenInputTemplate_AddsHtmlAttributesDictionary(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper("Hidden Value"); htmlHelper.ViewContext.ViewBag.htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes); Assert.Equal( expectedHtml, DefaultEditorTemplates.HiddenInputTemplate(htmlHelper)); } // MultilineText [Fact] public void MultilineTextTemplateTests() { Assert.Equal( "", DefaultEditorTemplates.MultilineTextTemplate(MakeHtmlHelper("", "Multiple" + Environment.NewLine + "Line" + Environment.NewLine + "Value!"))); Assert.Equal( "", DefaultEditorTemplates.MultilineTextTemplate(MakeHtmlHelper("", ""))); } public static TheoryDataSet MultilineTextTemplateHtmlAttributeData { get { return new TheoryDataSet { { new { @class = "form-control" }, "" }, { new { @class = "form-control", custom = "foo" }, "" } }; } } [Theory] [PropertyData("MultilineTextTemplateHtmlAttributeData")] public void MultilineTextTemplate_AddsHtmlAttributes(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper("", "Multiple" + Environment.NewLine + "Line" + Environment.NewLine + "Value!"); htmlHelper.ViewContext.ViewBag.htmlAttributes = htmlAttributes; Assert.Equal( expectedHtml, DefaultEditorTemplates.MultilineTextTemplate(htmlHelper)); } [Theory] [PropertyData("MultilineTextTemplateHtmlAttributeData")] public void MultilineTextTemplate_AddsHtmlAttributesDictionary(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper("", "Multiple" + Environment.NewLine + "Line" + Environment.NewLine + "Value!"); htmlHelper.ViewContext.ViewBag.htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes); Assert.Equal( expectedHtml, DefaultEditorTemplates.MultilineTextTemplate(htmlHelper)); } // ObjectTemplate private static string SpyCallback(HtmlHelper html, ModelMetadata metadata, string htmlFieldName, string templateName, DataBoundControlMode mode, object additionalViewData) { return String.Format("Model = {0}, ModelType = {1}, PropertyName = {2}, HtmlFieldName = {3}, TemplateName = {4}, Mode = {5}, AdditionalViewData = {6}", metadata.Model ?? "(null)", metadata.ModelType == null ? "(null)" : metadata.ModelType.FullName, metadata.PropertyName ?? "(null)", htmlFieldName == String.Empty ? "(empty)" : htmlFieldName ?? "(null)", templateName ?? "(null)", mode, AnonymousObject.Inspect(additionalViewData)); } private class ObjectTemplateModel { public ObjectTemplateModel() { ComplexInnerModel = new object(); } public string Property1 { get; set; } public string Property2 { get; set; } public object ComplexInnerModel { get; set; } } [Fact] public void ObjectTemplateEditsSimplePropertiesOnObjectByDefault() { string expected = "
" + Environment.NewLine + "
Model = p1, ModelType = System.String, PropertyName = Property1, HtmlFieldName = Property1, TemplateName = (null), Mode = Edit, AdditionalViewData = (null)
" + Environment.NewLine + "
" + Environment.NewLine + "
Model = (null), ModelType = System.String, PropertyName = Property2, HtmlFieldName = Property2, TemplateName = (null), Mode = Edit, AdditionalViewData = (null)
" + Environment.NewLine; // Arrange ObjectTemplateModel model = new ObjectTemplateModel { Property1 = "p1", Property2 = null }; HtmlHelper html = MakeHtmlHelper(model); // Act string result = DefaultEditorTemplates.ObjectTemplate(html, SpyCallback); // Assert Assert.Equal(expected, result); } [Fact] public void ObjectTemplate_AddsHtmlAttributes() { string expected = "
" + Environment.NewLine + "
" + Environment.NewLine + "
" + Environment.NewLine + "
" + Environment.NewLine; // Arrange ObjectTemplateModel model = new ObjectTemplateModel { Property1 = "p1", Property2 = null }; HtmlHelper html = MakeHtmlHelper(model); html.ViewContext.ViewBag.htmlAttributes = new { @class = "form-control", foo = "bar" }; // Act string result = RunWithoutViewEngine(() => DefaultEditorTemplates.ObjectTemplate(html)); // Assert Assert.Equal(expected, result); } [Fact] public void ObjectTemplateWithModelError() { string expected = "
" + Environment.NewLine + "
Model = p1, ModelType = System.String, PropertyName = Property1, HtmlFieldName = Property1, TemplateName = (null), Mode = Edit, AdditionalViewData = (null) Error Message
" + Environment.NewLine + "
" + Environment.NewLine + "
Model = (null), ModelType = System.String, PropertyName = Property2, HtmlFieldName = Property2, TemplateName = (null), Mode = Edit, AdditionalViewData = (null)
" + Environment.NewLine; // Arrange ObjectTemplateModel model = new ObjectTemplateModel { Property1 = "p1", Property2 = null }; HtmlHelper html = MakeHtmlHelper(model); html.ViewData.ModelState.AddModelError("FieldPrefix.Property1", "Error Message"); // Act string result = DefaultEditorTemplates.ObjectTemplate(html, SpyCallback); // Assert Assert.Equal(expected, result); } [Fact] public void ObjectTemplateWithDisplayNameMetadata() { string expected = "
Model = (null), ModelType = System.String, PropertyName = Property1, HtmlFieldName = Property1, TemplateName = (null), Mode = Edit, AdditionalViewData = (null)
" + Environment.NewLine + "
" + Environment.NewLine + "
Model = (null), ModelType = System.String, PropertyName = Property2, HtmlFieldName = Property2, TemplateName = (null), Mode = Edit, AdditionalViewData = (null)
" + Environment.NewLine; // Arrange ObjectTemplateModel model = new ObjectTemplateModel(); HtmlHelper html = MakeHtmlHelper(model); Mock provider = new Mock(); Func accessor = () => model; Mock metadata = new Mock(provider.Object, null, accessor, typeof(ObjectTemplateModel), null); ModelMetadata prop1Metadata = new ModelMetadata(provider.Object, typeof(ObjectTemplateModel), null, typeof(string), "Property1") { DisplayName = String.Empty }; ModelMetadata prop2Metadata = new ModelMetadata(provider.Object, typeof(ObjectTemplateModel), null, typeof(string), "Property2") { DisplayName = "Custom display name" }; html.ViewData.ModelMetadata = metadata.Object; metadata.Setup(p => p.Properties).Returns(() => new[] { prop1Metadata, prop2Metadata }); // Act string result = DefaultEditorTemplates.ObjectTemplate(html, SpyCallback); // Assert Assert.Equal(expected, result); } [Fact] public void ObjectTemplateWithShowForEditorMetadata() { string expected = "
" + Environment.NewLine + "
Model = (null), ModelType = System.String, PropertyName = Property1, HtmlFieldName = Property1, TemplateName = (null), Mode = Edit, AdditionalViewData = (null)
" + Environment.NewLine; // Arrange ObjectTemplateModel model = new ObjectTemplateModel(); HtmlHelper html = MakeHtmlHelper(model); Mock provider = new Mock(); Func accessor = () => model; Mock metadata = new Mock(provider.Object, null, accessor, typeof(ObjectTemplateModel), null); ModelMetadata prop1Metadata = new ModelMetadata(provider.Object, typeof(ObjectTemplateModel), null, typeof(string), "Property1") { ShowForEdit = true }; ModelMetadata prop2Metadata = new ModelMetadata(provider.Object, typeof(ObjectTemplateModel), null, typeof(string), "Property2") { ShowForEdit = false }; html.ViewData.ModelMetadata = metadata.Object; metadata.Setup(p => p.Properties).Returns(() => new[] { prop1Metadata, prop2Metadata }); // Act string result = DefaultEditorTemplates.ObjectTemplate(html, SpyCallback); // Assert Assert.Equal(expected, result); } [Fact] public void ObjectTemplatePreventsRecursionOnModelValue() { string expected = "
" + Environment.NewLine + "
Model = propValue2, ModelType = System.String, PropertyName = Property2, HtmlFieldName = Property2, TemplateName = (null), Mode = Edit, AdditionalViewData = (null)
" + Environment.NewLine; // Arrange ObjectTemplateModel model = new ObjectTemplateModel(); HtmlHelper html = MakeHtmlHelper(model); Mock provider = new Mock(); Func accessor = () => model; Mock metadata = new Mock(provider.Object, null, accessor, typeof(ObjectTemplateModel), null); ModelMetadata prop1Metadata = new ModelMetadata(provider.Object, typeof(ObjectTemplateModel), () => "propValue1", typeof(string), "Property1"); ModelMetadata prop2Metadata = new ModelMetadata(provider.Object, typeof(ObjectTemplateModel), () => "propValue2", typeof(string), "Property2"); html.ViewData.ModelMetadata = metadata.Object; metadata.Setup(p => p.Properties).Returns(() => new[] { prop1Metadata, prop2Metadata }); html.ViewData.TemplateInfo.VisitedObjects.Add("propValue1"); // Act string result = DefaultEditorTemplates.ObjectTemplate(html, SpyCallback); // Assert Assert.Equal(expected, result); } [Fact] public void ObjectTemplatePreventsRecursionOnModelTypeForNullModelValues() { string expected = "
" + Environment.NewLine + "
Model = propValue2, ModelType = System.String, PropertyName = Property2, HtmlFieldName = Property2, TemplateName = (null), Mode = Edit, AdditionalViewData = (null)
" + Environment.NewLine; // Arrange ObjectTemplateModel model = new ObjectTemplateModel(); HtmlHelper html = MakeHtmlHelper(model); Mock provider = new Mock(); Func accessor = () => model; Mock metadata = new Mock(provider.Object, null, accessor, typeof(ObjectTemplateModel), null); ModelMetadata prop1Metadata = new ModelMetadata(provider.Object, typeof(ObjectTemplateModel), null, typeof(string), "Property1"); ModelMetadata prop2Metadata = new ModelMetadata(provider.Object, typeof(ObjectTemplateModel), () => "propValue2", typeof(string), "Property2"); html.ViewData.ModelMetadata = metadata.Object; metadata.Setup(p => p.Properties).Returns(() => new[] { prop1Metadata, prop2Metadata }); html.ViewData.TemplateInfo.VisitedObjects.Add(typeof(string)); // Act string result = DefaultEditorTemplates.ObjectTemplate(html, SpyCallback); // Assert Assert.Equal(expected, result); } [Fact] public void ObjectTemplateDisplaysNullDisplayTextWithNullModelAndTemplateDepthGreaterThanOne() { // Arrange HtmlHelper html = MakeHtmlHelper(null); ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(ObjectTemplateModel)); metadata.NullDisplayText = "Null Display Text"; metadata.SimpleDisplayText = "Simple Display Text"; html.ViewData.ModelMetadata = metadata; html.ViewData.TemplateInfo.VisitedObjects.Add("foo"); html.ViewData.TemplateInfo.VisitedObjects.Add("bar"); // Act string result = DefaultEditorTemplates.ObjectTemplate(html, SpyCallback); // Assert Assert.Equal(metadata.NullDisplayText, result); } [Theory] [PropertyData("ConditionallyHtmlEncodedData", PropertyType = typeof(EncodedDataSets))] public void ObjectTemplateDisplaysSimpleDisplayTextWithNonNullModelTemplateDepthGreaterThanOne( string simpleDisplayText, bool htmlEncode, string expectedResult) { // Arrange ObjectTemplateModel model = new ObjectTemplateModel(); HtmlHelper html = MakeHtmlHelper(model); ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, typeof(ObjectTemplateModel)); html.ViewData.ModelMetadata = metadata; metadata.NullDisplayText = "Null Display Text"; metadata.SimpleDisplayText = simpleDisplayText; metadata.HtmlEncode = htmlEncode; html.ViewData.TemplateInfo.VisitedObjects.Add("foo"); html.ViewData.TemplateInfo.VisitedObjects.Add("bar"); // Act string result = DefaultEditorTemplates.ObjectTemplate(html, SpyCallback); // Assert Assert.Equal(expectedResult, result); } // PasswordTemplate [Fact] public void PasswordTemplateTests() { Assert.Equal( "", DefaultEditorTemplates.PasswordTemplate(MakeHtmlHelper("Value"))); Assert.Equal( "", DefaultEditorTemplates.PasswordTemplate(MakeHtmlHelper(""))); } [Fact] public void PasswordTemplate_ReturnsInputElement_IgnoresValues() { // Arrange var expected = ""; // Template ignores Model and FormattedModelValue. var helper = MakeHtmlHelper(model: "Model string", formattedModelValue: "Formatted string"); var viewData = helper.ViewData; var templateInfo = viewData.TemplateInfo; templateInfo.HtmlFieldPrefix = "FieldPrefix"; // Template ignores ModelState and ViewData. var valueProviderResult = new ValueProviderResult( "Raw model string", "Attempted model string", CultureInfo.InvariantCulture); viewData.ModelState.SetModelValue("FieldPrefix", valueProviderResult); viewData["FieldPrefix"] = "ViewData string"; // Act var result = DefaultEditorTemplates.PasswordTemplate(helper); // Assert Assert.Equal(expected, result); } [Fact] public void PasswordTemplate_ReturnsInputElement_UsesHtmlAttributes() { // Arrange var expected = ""; var helper = MakeHtmlHelper(model: null); var viewData = helper.ViewData; var templateInfo = viewData.TemplateInfo; templateInfo.HtmlFieldPrefix = "FieldPrefix"; viewData["htmlAttributes"] = new { @class = "super", value = "Html attributes string" }; // Act var result = DefaultEditorTemplates.PasswordTemplate(helper); // Assert Assert.Equal(expected, result); } public static TheoryDataSet PasswordTemplateHtmlAttributeData { get { return new TheoryDataSet { { new { @class = "form-control" }, "" }, { new { @class = "form-control", custom = "foo" }, "" } }; } } [Theory] [PropertyData("PasswordTemplateHtmlAttributeData")] public void PasswordTemplate_AddsHtmlAttributes(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper("Value"); htmlHelper.ViewContext.ViewBag.htmlAttributes = htmlAttributes; Assert.Equal( expectedHtml, DefaultEditorTemplates.PasswordTemplate(htmlHelper)); } [Theory] [PropertyData("PasswordTemplateHtmlAttributeData")] public void PasswordTemplate_AddsHtmlAttributesDictionary(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper("Value"); htmlHelper.ViewContext.ViewBag.htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes); Assert.Equal( expectedHtml, DefaultEditorTemplates.PasswordTemplate(htmlHelper)); } [Fact] public void ObjectTemplateWithHiddenHtml() { string expected = "Model = propValue1, ModelType = System.String, PropertyName = Property1, HtmlFieldName = Property1, TemplateName = (null), Mode = Edit, AdditionalViewData = (null)"; // Arrange ObjectTemplateModel model = new ObjectTemplateModel(); HtmlHelper html = MakeHtmlHelper(model); Mock provider = new Mock(); Func accessor = () => model; Mock metadata = new Mock(provider.Object, null, accessor, typeof(ObjectTemplateModel), null); ModelMetadata prop1Metadata = new ModelMetadata(provider.Object, typeof(ObjectTemplateModel), () => "propValue1", typeof(string), "Property1") { HideSurroundingHtml = true }; html.ViewData.ModelMetadata = metadata.Object; metadata.Setup(p => p.Properties).Returns(() => new[] { prop1Metadata }); // Act string result = DefaultEditorTemplates.ObjectTemplate(html, SpyCallback); // Assert Assert.Equal(expected, result); } [Fact] public void ObjectTemplateAllPropertiesFromEntityObjectAreHidden() { // Arrange HtmlHelper html = MakeHtmlHelper(new MyEntityObject()); // Act string result = DefaultEditorTemplates.ObjectTemplate(html, SpyCallback); // Assert Assert.Equal(String.Empty, result); } private class MyEntityObject : EntityObject { } // StringTemplate [Fact] public void StringTemplateTests() { Assert.Equal( "", DefaultEditorTemplates.StringTemplate(MakeHtmlHelper("Value"))); Assert.Equal( "alert('XSS!')</script>\" />", DefaultEditorTemplates.StringTemplate(MakeHtmlHelper(""))); } public static TheoryDataSet StringTemplateHtmlAttributeData { get { return new TheoryDataSet { { new { @class = "form-control" }, "" }, { new { @class = "form-control", custom = "foo" }, "" } }; } } [Theory] [PropertyData("StringTemplateHtmlAttributeData")] public void StringTemplate_AddsHtmlAttributes(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper("Value"); htmlHelper.ViewContext.ViewBag.htmlAttributes = htmlAttributes; Assert.Equal( expectedHtml, DefaultEditorTemplates.StringTemplate(htmlHelper)); } [Theory] [PropertyData("StringTemplateHtmlAttributeData")] public void StringTemplate_AddsHtmlAttributesDictionary(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper("Value"); htmlHelper.ViewContext.ViewBag.htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes); Assert.Equal( expectedHtml, DefaultEditorTemplates.StringTemplate(htmlHelper)); } // PhoneNumberInputTemplate [Fact] public void PhoneNumberInputTemplateTests() { Assert.Equal( "", DefaultEditorTemplates.PhoneNumberInputTemplate(MakeHtmlHelper("Value"))); Assert.Equal( "alert('XSS!')</script>\" />", DefaultEditorTemplates.PhoneNumberInputTemplate(MakeHtmlHelper(""))); } public static TheoryDataSet PhoneNumberInputTemplateHtmlAttributeData { get { return new TheoryDataSet { { new { @class = "form-control" }, "" }, { new { @class = "form-control", custom = "foo" }, "" } }; } } [Theory] [PropertyData("PhoneNumberInputTemplateHtmlAttributeData")] public void PhoneNumberInputTemplate_AddsHtmlAttributes(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper("Value"); htmlHelper.ViewContext.ViewBag.htmlAttributes = htmlAttributes; Assert.Equal( expectedHtml, DefaultEditorTemplates.PhoneNumberInputTemplate(htmlHelper)); } [Theory] [PropertyData("PhoneNumberInputTemplateHtmlAttributeData")] public void PhoneNumberInputTemplate_AddsHtmlAttributesDictionary(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper("Value"); htmlHelper.ViewContext.ViewBag.htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes); Assert.Equal( expectedHtml, DefaultEditorTemplates.PhoneNumberInputTemplate(htmlHelper)); } // UrlInputTemplate [Fact] public void UrlInputTemplateTests() { Assert.Equal( "", DefaultEditorTemplates.UrlInputTemplate(MakeHtmlHelper("Value"))); Assert.Equal( "alert('XSS!')</script>\" />", DefaultEditorTemplates.UrlInputTemplate(MakeHtmlHelper(""))); } public static TheoryDataSet UrlInputTemplateHtmlAttributeData { get { return new TheoryDataSet { { new { @class = "form-control" }, "" }, { new { @class = "form-control", custom = "foo" }, "" } }; } } [Theory] [PropertyData("UrlInputTemplateHtmlAttributeData")] public void UrlInputTemplate_AddsHtmlAttributes(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper("Value"); htmlHelper.ViewContext.ViewBag.htmlAttributes = htmlAttributes; Assert.Equal( expectedHtml, DefaultEditorTemplates.UrlInputTemplate(htmlHelper)); } [Theory] [PropertyData("UrlInputTemplateHtmlAttributeData")] public void UrlInputTemplate_AddsHtmlAttributesDictionary(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper("Value"); htmlHelper.ViewContext.ViewBag.htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes); Assert.Equal( expectedHtml, DefaultEditorTemplates.UrlInputTemplate(htmlHelper)); } // EmailAddressInputTemplate [Fact] public void EmailAddressTemplateTests() { Assert.Equal( "", DefaultEditorTemplates.EmailAddressInputTemplate(MakeHtmlHelper("Value"))); Assert.Equal( "alert('XSS!')</script>\" />", DefaultEditorTemplates.EmailAddressInputTemplate(MakeHtmlHelper(""))); } public static TheoryDataSet EmailAddressInputTemplateHtmlAttributeData { get { return new TheoryDataSet { { new { @class = "form-control" }, "" }, { new { @class = "form-control", custom = "foo" }, "" } }; } } [Theory] [PropertyData("EmailAddressInputTemplateHtmlAttributeData")] public void EmailAddressInputTemplate_AddsHtmlAttributes(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper("Value"); htmlHelper.ViewContext.ViewBag.htmlAttributes = htmlAttributes; Assert.Equal( expectedHtml, DefaultEditorTemplates.EmailAddressInputTemplate(htmlHelper)); } [Theory] [PropertyData("EmailAddressInputTemplateHtmlAttributeData")] public void EmailAddressInputTemplate_AddsHtmlAttributesDictionary(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper("Value"); htmlHelper.ViewContext.ViewBag.htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes); Assert.Equal( expectedHtml, DefaultEditorTemplates.EmailAddressInputTemplate(htmlHelper)); } [Fact] public void DateTimeInputTemplateTests() { var type = "datetime"; Assert.Equal( GetExpectedInputTag(type, "Value"), DefaultEditorTemplates.DateTimeInputTemplate(MakeHtmlHelper("Value"))); Assert.Equal( GetExpectedInputTag(type, "<script>alert('XSS!')</script>"), DefaultEditorTemplates.DateTimeInputTemplate(MakeHtmlHelper(""))); var epocInLocalTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).ToLocalTime(); var helper = MakeHtmlHelper(epocInLocalTime); Assert.Equal( GetExpectedInputTag(type, epocInLocalTime.ToString(CultureInfo.CurrentCulture)), DefaultEditorTemplates.DateTimeInputTemplate(helper)); helper.Html5DateRenderingMode = Html5DateRenderingMode.Rfc3339; Assert.Equal( GetExpectedInputTag(type, epocInLocalTime.ToString("yyyy-MM-ddTHH:mm:ss.fffK")), DefaultEditorTemplates.DateTimeInputTemplate(helper)); // Override FormattedModelValue and let helper think this string came from a default [DataType] attribute. helper.ViewData.TemplateInfo.FormattedModelValue = "Another string"; Assert.Equal( GetExpectedInputTag(type, epocInLocalTime.ToString("yyyy-MM-ddTHH:mm:ss.fffK")), DefaultEditorTemplates.DateTimeInputTemplate(helper)); // Override again but tell helper this string was explicitly user-provided. helper.ViewData.ModelMetadata.HasNonDefaultEditFormat = true; helper.ViewData.TemplateInfo.FormattedModelValue = "Another string"; Assert.Equal( GetExpectedInputTag(type, "Another string"), DefaultEditorTemplates.DateTimeInputTemplate(helper)); } public static TheoryDataSet DateTimeInputTemplateHtmlAttributeData { get { return new TheoryDataSet { { new { @class = "form-control" }, "" }, { new { @class = "form-control", custom = "foo" }, "" } }; } } [Theory] [PropertyData("DateTimeInputTemplateHtmlAttributeData")] public void DateTimeInputTemplate_AddsHtmlAttributes(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper("Value"); htmlHelper.ViewContext.ViewBag.htmlAttributes = htmlAttributes; Assert.Equal( expectedHtml, DefaultEditorTemplates.DateTimeInputTemplate(htmlHelper)); } [Theory] [PropertyData("DateTimeInputTemplateHtmlAttributeData")] public void DateTimeInputTemplate_AddsHtmlAttributesDictionary(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper("Value"); htmlHelper.ViewContext.ViewBag.htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes); Assert.Equal( expectedHtml, DefaultEditorTemplates.DateTimeInputTemplate(htmlHelper)); } [Fact] public void DateTimeLocalInputTemplateTests() { var type = "datetime-local"; Assert.Equal( GetExpectedInputTag(type, "Value"), DefaultEditorTemplates.DateTimeLocalInputTemplate(MakeHtmlHelper("Value"))); Assert.Equal( GetExpectedInputTag(type, "<script>alert('XSS!')</script>"), DefaultEditorTemplates.DateTimeLocalInputTemplate(MakeHtmlHelper(""))); var epocInLocalTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).ToLocalTime(); var helper = MakeHtmlHelper(epocInLocalTime); Assert.Equal( GetExpectedInputTag(type, epocInLocalTime.ToString(CultureInfo.CurrentCulture)), DefaultEditorTemplates.DateTimeLocalInputTemplate(helper)); helper.Html5DateRenderingMode = Html5DateRenderingMode.Rfc3339; Assert.Equal( GetExpectedInputTag(type, epocInLocalTime.ToString("yyyy-MM-ddTHH:mm:ss.fff")), DefaultEditorTemplates.DateTimeLocalInputTemplate(helper)); // Override FormattedModelValue and let helper think this string came from a default [DataType] attribute. helper.ViewData.TemplateInfo.FormattedModelValue = "Another string"; Assert.Equal( GetExpectedInputTag(type, epocInLocalTime.ToString("yyyy-MM-ddTHH:mm:ss.fff")), DefaultEditorTemplates.DateTimeLocalInputTemplate(helper)); // Override again but tell helper this string was explicitly user-provided. helper.ViewData.ModelMetadata.HasNonDefaultEditFormat = true; helper.ViewData.TemplateInfo.FormattedModelValue = "Another string"; Assert.Equal( GetExpectedInputTag(type, "Another string"), DefaultEditorTemplates.DateTimeLocalInputTemplate(helper)); } public static TheoryDataSet DateTimeLocalInputTemplateHtmlAttributeData { get { return new TheoryDataSet { { new { @class = "form-control" }, "" }, { new { @class = "form-control", custom = "foo" }, "" } }; } } [Theory] [PropertyData("DateTimeLocalInputTemplateHtmlAttributeData")] public void DateTimeLocalInputTemplate_AddsHtmlAttributes(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper("Value"); htmlHelper.ViewContext.ViewBag.htmlAttributes = htmlAttributes; Assert.Equal( expectedHtml, DefaultEditorTemplates.DateTimeLocalInputTemplate(htmlHelper)); } [Theory] [PropertyData("DateTimeLocalInputTemplateHtmlAttributeData")] public void DateTimeLocalInputTemplate_AddsHtmlAttributesDictionary(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper("Value"); htmlHelper.ViewContext.ViewBag.htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes); Assert.Equal( expectedHtml, DefaultEditorTemplates.DateTimeLocalInputTemplate(htmlHelper)); } [Fact] public void DateInputTemplateTests() { var type = "date"; Assert.Equal( GetExpectedInputTag(type, "Value"), DefaultEditorTemplates.DateInputTemplate(MakeHtmlHelper("Value"))); Assert.Equal( GetExpectedInputTag(type, "<script>alert('XSS!')</script>"), DefaultEditorTemplates.DateInputTemplate(MakeHtmlHelper(""))); var epocInLocalTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).ToLocalTime(); var helper = MakeHtmlHelper(epocInLocalTime); Assert.Equal( GetExpectedInputTag(type, epocInLocalTime.ToString(CultureInfo.CurrentCulture)), DefaultEditorTemplates.DateInputTemplate(helper)); helper.Html5DateRenderingMode = Html5DateRenderingMode.Rfc3339; Assert.Equal( GetExpectedInputTag(type, epocInLocalTime.ToString("yyyy-MM-dd")), DefaultEditorTemplates.DateInputTemplate(helper)); // Override FormattedModelValue and let helper think this string came from a default [DataType] attribute. helper.ViewData.TemplateInfo.FormattedModelValue = "Another string"; Assert.Equal( GetExpectedInputTag(type, epocInLocalTime.ToString("yyyy-MM-dd")), DefaultEditorTemplates.DateInputTemplate(helper)); // Override again but tell helper this string was explicitly user-provided. helper.ViewData.ModelMetadata.HasNonDefaultEditFormat = true; helper.ViewData.TemplateInfo.FormattedModelValue = "Another string"; Assert.Equal( GetExpectedInputTag(type, "Another string"), DefaultEditorTemplates.DateInputTemplate(helper)); } public static TheoryDataSet DateInputTemplateHtmlAttributeData { get { return new TheoryDataSet { { new { @class = "form-control" }, "" }, { new { @class = "form-control", custom = "foo" }, "" } }; } } [Theory] [PropertyData("DateInputTemplateHtmlAttributeData")] public void DateInputTemplate_AddsHtmlAttributes(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper("Value"); htmlHelper.ViewContext.ViewBag.htmlAttributes = htmlAttributes; Assert.Equal( expectedHtml, DefaultEditorTemplates.DateInputTemplate(htmlHelper)); } [Theory] [PropertyData("DateInputTemplateHtmlAttributeData")] public void DateInputTemplate_AddsHtmlAttributesDictionary(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper("Value"); htmlHelper.ViewContext.ViewBag.htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes); Assert.Equal( expectedHtml, DefaultEditorTemplates.DateInputTemplate(htmlHelper)); } [Fact] public void TimeInputTemplateTests() { var type = "time"; Assert.Equal( GetExpectedInputTag(type, "Value"), DefaultEditorTemplates.TimeInputTemplate(MakeHtmlHelper("Value"))); Assert.Equal( GetExpectedInputTag(type, "<script>alert('XSS!')</script>"), DefaultEditorTemplates.TimeInputTemplate(MakeHtmlHelper(""))); var epocInLocalTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).ToLocalTime(); var helper = MakeHtmlHelper(epocInLocalTime); Assert.Equal( GetExpectedInputTag(type, epocInLocalTime.ToString(CultureInfo.CurrentCulture)), DefaultEditorTemplates.TimeInputTemplate(helper)); helper.Html5DateRenderingMode = Html5DateRenderingMode.Rfc3339; Assert.Equal( GetExpectedInputTag(type, epocInLocalTime.ToString("HH:mm:ss.fff")), DefaultEditorTemplates.TimeInputTemplate(helper)); // Override FormattedModelValue and let helper think this string came from a default [DataType] attribute. helper.ViewData.TemplateInfo.FormattedModelValue = "Another string"; Assert.Equal( GetExpectedInputTag(type, epocInLocalTime.ToString("HH:mm:ss.fff")), DefaultEditorTemplates.TimeInputTemplate(helper)); // Override again but tell helper this string was explicitly user-provided. helper.ViewData.ModelMetadata.HasNonDefaultEditFormat = true; helper.ViewData.TemplateInfo.FormattedModelValue = "Another string"; Assert.Equal( GetExpectedInputTag(type, "Another string"), DefaultEditorTemplates.TimeInputTemplate(helper)); } public static TheoryDataSet TimeInputTemplateHtmlAttributeData { get { return new TheoryDataSet { { new { @class = "form-control" }, "" }, { new { @class = "form-control", custom = "foo" }, "" } }; } } [Theory] [PropertyData("TimeInputTemplateHtmlAttributeData")] public void TimeInputTemplate_AddsHtmlAttributes(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper("Value"); htmlHelper.ViewContext.ViewBag.htmlAttributes = htmlAttributes; Assert.Equal( expectedHtml, DefaultEditorTemplates.TimeInputTemplate(htmlHelper)); } [Theory] [PropertyData("TimeInputTemplateHtmlAttributeData")] public void TimeInputTemplate_AddsHtmlAttributesDictionary(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper("Value"); htmlHelper.ViewContext.ViewBag.htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes); Assert.Equal( expectedHtml, DefaultEditorTemplates.TimeInputTemplate(htmlHelper)); } // NumberInputTemplate [Fact] public void NumberInputTemplateTests() { Assert.Equal( "", DefaultEditorTemplates.NumberInputTemplate(MakeHtmlHelper("Value"))); Assert.Equal( "alert('XSS!')</script>\" />", DefaultEditorTemplates.NumberInputTemplate(MakeHtmlHelper(""))); } public static TheoryDataSet NumberInputTemplateHtmlAttributeData { get { return new TheoryDataSet { { new { @class = "form-control" }, "" }, { new { @class = "form-control", custom = "foo" }, "" } }; } } [Theory] [PropertyData("NumberInputTemplateHtmlAttributeData")] public void NumberInputTemplate_AddsHtmlAttributes(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper(10); htmlHelper.ViewContext.ViewBag.htmlAttributes = htmlAttributes; Assert.Equal( expectedHtml, DefaultEditorTemplates.NumberInputTemplate(htmlHelper)); } [Theory] [PropertyData("NumberInputTemplateHtmlAttributeData")] public void NumberInputTemplate_AddsHtmlAttributesDictionary(object htmlAttributes, string expectedHtml) { var htmlHelper = MakeHtmlHelper(10); htmlHelper.ViewContext.ViewBag.htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes); Assert.Equal( expectedHtml, DefaultEditorTemplates.NumberInputTemplate(htmlHelper)); } // ColorInputTemplate [Fact] public void ColorInputTemplateTests() { Assert.Equal( "", DefaultEditorTemplates.ColorInputTemplate(MakeHtmlHelper("#33F4CC"))); var color = Color.FromArgb(0x33, 0xf4, 0xcc); Assert.Equal( "", DefaultEditorTemplates.ColorInputTemplate(MakeHtmlHelper(color))); Assert.Equal( "alert('XSS!')</script>\" />", DefaultEditorTemplates.ColorInputTemplate(MakeHtmlHelper(""))); } public static TheoryDataSet ColorInputTemplateHtmlAttributeData { get { return new TheoryDataSet { { new { @class = "form-control" }, "" }, { new { @class = "form-control", custom = "foo" }, "" } }; } } [Theory] [PropertyData("ColorInputTemplateHtmlAttributeData")] public void ColorInputTemplate_AddsHtmlAttributes(object htmlAttributes, string expectedHtml) { var color = Color.FromArgb(0x33, 0xf4, 0xcc); var htmlHelper = MakeHtmlHelper(color); htmlHelper.ViewContext.ViewBag.htmlAttributes = htmlAttributes; Assert.Equal( expectedHtml, DefaultEditorTemplates.ColorInputTemplate(htmlHelper)); } [Theory] [PropertyData("ColorInputTemplateHtmlAttributeData")] public void ColorInputTemplate_AddsHtmlAttributesDictionary(object htmlAttributes, string expectedHtml) { var color = Color.FromArgb(0x33, 0xf4, 0xcc); var htmlHelper = MakeHtmlHelper(color); htmlHelper.ViewContext.ViewBag.htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes); Assert.Equal( expectedHtml, DefaultEditorTemplates.ColorInputTemplate(htmlHelper)); } // Helpers private static string GetExpectedInputTag(string type, string value) { return string.Format("", type, value); } private HtmlHelper MakeHtmlHelper(object model) { return MakeHtmlHelper(model, model); } private HtmlHelper MakeHtmlHelper(object model, object formattedModelValue) { ViewDataDictionary viewData = new ViewDataDictionary(model); viewData.TemplateInfo.HtmlFieldPrefix = "FieldPrefix"; viewData.TemplateInfo.FormattedModelValue = formattedModelValue; viewData.ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(() => model, typeof(TModel)); Mock mockHttpContext = new Mock(); mockHttpContext.Setup(o => o.Items).Returns(new Hashtable()); ViewContext viewContext = new ViewContext(new ControllerContext { HttpContext = mockHttpContext.Object }, new DummyView(), viewData, new TempDataDictionary(), new StringWriter()) { HttpContext = mockHttpContext.Object }; // A new helper instance, is executing within a new scope, so it needs to be reset. ScopeStorage.CurrentProvider.CurrentScope = new ScopeStorageDictionary(); return new HtmlHelper(viewContext, new SimpleViewDataContainer(viewData)); } private class DummyView : IView { public void Render(ViewContext viewContext, TextWriter writer) { throw new NotImplementedException(); } } private string RunWithoutViewEngine(Func testCode) { lock (ViewEngines.Engines) { List viewEngines = new List(ViewEngines.Engines); ViewEngines.Engines.Clear(); try { return testCode(); } finally { foreach (var engine in viewEngines) { ViewEngines.Engines.Add(engine); } } } } public void Dispose() { // Reset ScopeStorage (written via e.g. ScopeStorage.CurrentProvider.CurrentScope) between tests to avoid unexpected interactions. ScopeStorage.CurrentProvider = new StaticScopeStorageProvider(); ScopeStorage.GlobalScope.Clear(); } } }