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

1161 lines
55 KiB
C#

// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
using System.Web.Routing;
using System.Web.UI.WebControls;
using Microsoft.Web.UnitTestUtil;
using Moq;
using Xunit;
using Assert = Microsoft.TestCommon.AssertEx;
namespace System.Web.Mvc.Html.Test
{
public class TemplateHelpersTest
{
// ExecuteTemplate
private class ExecuteTemplateModel
{
public string MyProperty { get; set; }
public Nullable<int> MyNullableProperty { get; set; }
}
[Fact]
public void ExecuteTemplateCallsGetViewNamesWithProvidedTemplateNameAndMetadataInformation()
{
using (new MockViewEngine())
{
// Arrange
HtmlHelper html = MakeHtmlHelper(new ExecuteTemplateModel());
ModelMetadata metadata = ModelMetadata.FromStringExpression("MyProperty", html.ViewData);
metadata.TemplateHint = "templateHint";
metadata.DataTypeName = "dataType";
string[] hints = null;
// Act
TemplateHelpers.ExecuteTemplate(
html, MakeViewData(html, metadata), "templateName", DataBoundControlMode.ReadOnly,
(_metadata, _hints) =>
{
hints = _hints;
return new[] { "String" };
},
TemplateHelpers.GetDefaultActions);
// Assert
Assert.NotNull(hints);
Assert.Equal(3, hints.Length);
Assert.Equal("templateName", hints[0]);
Assert.Equal("templateHint", hints[1]);
Assert.Equal("dataType", hints[2]);
}
}
[Fact]
public void ExecuteTemplateUsesViewFromViewEngineInReadOnlyMode()
{
using (MockViewEngine engine = new MockViewEngine())
{
// Arrange
HtmlHelper html = MakeHtmlHelper(new ExecuteTemplateModel { MyProperty = "Hello" });
ModelMetadata metadata = ModelMetadata.FromStringExpression("MyProperty", html.ViewData);
ViewContext callbackViewContext = null;
engine.Engine.Setup(e => e.FindPartialView(html.ViewContext, "DisplayTemplates/String", true))
.Returns(new ViewEngineResult(engine.View.Object, engine.Engine.Object))
.Verifiable();
engine.View.Setup(v => v.Render(It.IsAny<ViewContext>(), It.IsAny<TextWriter>()))
.Callback<ViewContext, TextWriter>((vc, tw) =>
{
callbackViewContext = vc;
tw.Write("View Text");
})
.Verifiable();
ViewDataDictionary viewData = MakeViewData(html, metadata);
// Act
string result = TemplateHelpers.ExecuteTemplate(
html, viewData, "templateName", DataBoundControlMode.ReadOnly,
delegate { return new[] { "String" }; },
TemplateHelpers.GetDefaultActions);
// Assert
engine.Engine.Verify();
engine.View.Verify();
Assert.Equal("View Text", result);
Assert.Same(engine.View.Object, callbackViewContext.View);
Assert.Same(viewData, callbackViewContext.ViewData);
Assert.Same(html.ViewContext.TempData, callbackViewContext.TempData);
TemplateHelpers.ActionCacheViewItem cacheItem = TemplateHelpers.GetActionCache(html)["DisplayTemplates/String"] as TemplateHelpers.ActionCacheViewItem;
Assert.NotNull(cacheItem);
Assert.Equal("DisplayTemplates/String", cacheItem.ViewName);
}
}
[Fact]
public void ExecuteTemplateUsesViewFromViewEngineInEditMode()
{
using (MockViewEngine engine = new MockViewEngine())
{
// Arrange
HtmlHelper html = MakeHtmlHelper(new ExecuteTemplateModel { MyProperty = "Hello" });
ModelMetadata metadata = ModelMetadata.FromStringExpression("MyProperty", html.ViewData);
ViewContext callbackViewContext = null;
engine.Engine.Setup(e => e.FindPartialView(html.ViewContext, "EditorTemplates/String", true))
.Returns(new ViewEngineResult(engine.View.Object, engine.Engine.Object))
.Verifiable();
engine.View.Setup(v => v.Render(It.IsAny<ViewContext>(), It.IsAny<TextWriter>()))
.Callback<ViewContext, TextWriter>((vc, tw) =>
{
callbackViewContext = vc;
tw.Write("View Text");
})
.Verifiable();
ViewDataDictionary viewData = MakeViewData(html, metadata);
// Act
string result = TemplateHelpers.ExecuteTemplate(
html, viewData, "templateName", DataBoundControlMode.Edit,
delegate { return new[] { "String" }; },
TemplateHelpers.GetDefaultActions);
// Assert
engine.Engine.Verify();
engine.View.Verify();
Assert.Equal("View Text", result);
Assert.Same(engine.View.Object, callbackViewContext.View);
Assert.Same(viewData, callbackViewContext.ViewData);
Assert.Same(html.ViewContext.TempData, callbackViewContext.TempData);
TemplateHelpers.ActionCacheViewItem cacheItem = TemplateHelpers.GetActionCache(html)["EditorTemplates/String"] as TemplateHelpers.ActionCacheViewItem;
Assert.NotNull(cacheItem);
Assert.Equal("EditorTemplates/String", cacheItem.ViewName);
}
}
[Fact]
public void ExecuteTemplateUsesViewFromDefaultActionsInReadOnlyMode()
{
using (MockViewEngine engine = new MockViewEngine())
{
// Arrange
HtmlHelper html = MakeHtmlHelper(new ExecuteTemplateModel { MyProperty = "Hello" });
ModelMetadata metadata = ModelMetadata.FromStringExpression("MyProperty", html.ViewData);
engine.Engine.Setup(e => e.FindPartialView(html.ViewContext, "DisplayTemplates/String", It.IsAny<bool>()))
.Returns(new ViewEngineResult(new string[0]))
.Verifiable();
ViewDataDictionary viewData = MakeViewData(html, metadata);
// Act
TemplateHelpers.ExecuteTemplate(
html, viewData, "templateName", DataBoundControlMode.ReadOnly,
delegate { return new[] { "String" }; },
TemplateHelpers.GetDefaultActions);
// Assert
engine.Engine.Verify();
TemplateHelpers.ActionCacheCodeItem cacheItem = TemplateHelpers.GetActionCache(html)["DisplayTemplates/String"] as TemplateHelpers.ActionCacheCodeItem;
Assert.NotNull(cacheItem);
Assert.Equal(DefaultDisplayTemplates.StringTemplate, cacheItem.Action);
}
}
[Fact]
public void ExecuteTemplateUsesViewFromDefaultActionsInEditMode()
{
using (MockViewEngine engine = new MockViewEngine())
{
// Arrange
HtmlHelper html = MakeHtmlHelper(new ExecuteTemplateModel { MyProperty = "Hello" });
ModelMetadata metadata = ModelMetadata.FromStringExpression("MyProperty", html.ViewData);
engine.Engine.Setup(e => e.FindPartialView(html.ViewContext, "EditorTemplates/String", It.IsAny<bool>()))
.Returns(new ViewEngineResult(new string[0]))
.Verifiable();
ViewDataDictionary viewData = MakeViewData(html, metadata);
// Act
TemplateHelpers.ExecuteTemplate(
html, viewData, "templateName", DataBoundControlMode.Edit,
delegate { return new[] { "String" }; },
TemplateHelpers.GetDefaultActions);
// Assert
engine.Engine.Verify();
TemplateHelpers.ActionCacheCodeItem cacheItem = TemplateHelpers.GetActionCache(html)["EditorTemplates/String"] as TemplateHelpers.ActionCacheCodeItem;
Assert.NotNull(cacheItem);
Assert.Equal(DefaultEditorTemplates.StringTemplate, cacheItem.Action);
}
}
[Fact]
public void ExecuteTemplatePrefersExistingActionCacheItem()
{
using (MockViewEngine engine = new MockViewEngine())
{
// Arrange
HtmlHelper html = MakeHtmlHelper(new ExecuteTemplateModel { MyProperty = "Hello" });
ModelMetadata metadata = ModelMetadata.FromStringExpression("MyProperty", html.ViewData);
ViewDataDictionary viewData = MakeViewData(html, metadata);
TemplateHelpers.GetActionCache(html).Add("EditorTemplates/String",
new TemplateHelpers.ActionCacheCodeItem { Action = _ => "Action Text" });
// Act
string result = TemplateHelpers.ExecuteTemplate(
html, viewData, "templateName", DataBoundControlMode.Edit,
delegate { return new[] { "String" }; },
TemplateHelpers.GetDefaultActions);
// Assert
engine.Engine.Verify();
engine.Engine.Verify(e => e.FindPartialView(It.IsAny<ControllerContext>(), It.IsAny<string>(), It.IsAny<bool>()), Times.Never());
Assert.Equal("Action Text", result);
}
}
[Fact]
public void ExecuteTemplateThrowsWhenNoTemplatesMatch()
{
using (new MockViewEngine())
{
// Arrange
HtmlHelper html = MakeHtmlHelper(new ExecuteTemplateModel { MyProperty = "Hello" });
ModelMetadata metadata = ModelMetadata.FromStringExpression("MyProperty", html.ViewData);
ViewDataDictionary viewData = MakeViewData(html, metadata);
// Act & Assert
Assert.Throws<InvalidOperationException>(
() => TemplateHelpers.ExecuteTemplate(html, viewData, "templateName", DataBoundControlMode.Edit, delegate { return new string[0]; }, TemplateHelpers.GetDefaultActions),
"Unable to locate an appropriate template for type System.String.");
}
}
[Fact]
public void ExecuteTemplateCreatesNewHtmlHelperWithCorrectViewDataForDefaultAction()
{
using (MockViewEngine engine = new MockViewEngine(false))
{
// Arrange
HtmlHelper html = MakeHtmlHelper(new ExecuteTemplateModel { MyProperty = "Hello" });
ModelMetadata metadata = ModelMetadata.FromStringExpression("MyProperty", html.ViewData);
ViewDataDictionary viewData = MakeViewData(html, metadata);
HtmlHelper passedHtmlHelper = null;
// Act
TemplateHelpers.ExecuteTemplate(
html, viewData, "templateName", DataBoundControlMode.Edit,
delegate { return new[] { "String" }; },
delegate
{
return new Dictionary<string, Func<HtmlHelper, string>>
{
{
"String", _htmlHelper =>
{
passedHtmlHelper = _htmlHelper;
return "content";
}
}
};
});
// Assert
Assert.NotNull(passedHtmlHelper);
Assert.Same(passedHtmlHelper.ViewData, passedHtmlHelper.ViewContext.ViewData);
Assert.NotSame(html.ViewData, passedHtmlHelper.ViewData);
}
}
[Fact]
public void ExecuteTemplateCreatesNewHtmlHelperWithCorrectViewDataForCachedAction()
{
using (MockViewEngine engine = new MockViewEngine())
{
// Arrange
HtmlHelper html = MakeHtmlHelper(new ExecuteTemplateModel { MyProperty = "Hello" });
ModelMetadata metadata = ModelMetadata.FromStringExpression("MyProperty", html.ViewData);
ViewDataDictionary viewData = MakeViewData(html, metadata);
HtmlHelper passedHtmlHelper = null;
TemplateHelpers.GetActionCache(html).Add(
"EditorTemplates/String",
new TemplateHelpers.ActionCacheCodeItem
{
Action = _htmlHelper =>
{
passedHtmlHelper = _htmlHelper;
return "content";
}
});
// Act
string result = TemplateHelpers.ExecuteTemplate(
html, viewData, "templateName", DataBoundControlMode.Edit,
delegate { return new[] { "String" }; },
TemplateHelpers.GetDefaultActions);
// Assert
Assert.NotNull(passedHtmlHelper);
Assert.Same(passedHtmlHelper.ViewData, passedHtmlHelper.ViewContext.ViewData);
Assert.NotSame(html.ViewData, passedHtmlHelper.ViewData);
}
}
// GetActionCache
[Fact]
public void CacheIsCreatedIfNotPresent()
{
// Arrange
Hashtable items = new Hashtable();
Mock<HttpContextBase> context = new Mock<HttpContextBase>();
context.Setup(c => c.Items).Returns(items);
Mock<ViewContext> viewContext = new Mock<ViewContext>();
viewContext.Setup(c => c.HttpContext).Returns(context.Object);
Mock<IViewDataContainer> viewDataContainer = new Mock<IViewDataContainer>();
HtmlHelper helper = new HtmlHelper(viewContext.Object, viewDataContainer.Object);
// Act
Dictionary<string, TemplateHelpers.ActionCacheItem> cache = TemplateHelpers.GetActionCache(helper);
// Assert
Assert.NotNull(cache);
Assert.Empty(cache);
Assert.Contains((object)cache, items.Values.OfType<object>());
}
[Fact]
public void CacheIsReusedIfPresent()
{
// Arrange
Hashtable items = new Hashtable();
Mock<HttpContextBase> context = new Mock<HttpContextBase>();
context.Setup(c => c.Items).Returns(items);
Mock<ViewContext> viewContext = new Mock<ViewContext>();
viewContext.Setup(c => c.HttpContext).Returns(context.Object);
Mock<IViewDataContainer> viewDataContainer = new Mock<IViewDataContainer>();
HtmlHelper helper = new HtmlHelper(viewContext.Object, viewDataContainer.Object);
// Act
Dictionary<string, TemplateHelpers.ActionCacheItem> cache1 = TemplateHelpers.GetActionCache(helper);
Dictionary<string, TemplateHelpers.ActionCacheItem> cache2 = TemplateHelpers.GetActionCache(helper);
// Assert
Assert.NotNull(cache1);
Assert.Same(cache1, cache2);
}
// GetViewNames
[Fact]
public void GetViewNamesFullOrderingOfBuiltInValueType()
{
// Arrange
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(double));
// Act
List<string> result = TemplateHelpers.GetViewNames(metadata, "UIHint", "DataType").ToList();
// Assert
Assert.Equal(4, result.Count);
Assert.Equal("UIHint", result[0]);
Assert.Equal("DataType", result[1]);
Assert.Equal("Double", result[2]);
Assert.Equal("String", result[3]);
}
[Fact]
public void GetViewNamesFullOrderingOfComplexType()
{
// Arrange
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(HttpWebRequest));
// Act
List<string> result = TemplateHelpers.GetViewNames(metadata, "UIHint", "DataType").ToList();
// Assert
Assert.Equal(6, result.Count);
Assert.Equal("UIHint", result[0]);
Assert.Equal("DataType", result[1]);
Assert.Equal("HttpWebRequest", result[2]);
Assert.Equal("WebRequest", result[3]);
Assert.Equal("MarshalByRefObject", result[4]);
Assert.Equal("Object", result[5]);
}
[Fact]
public void GetViewNamesFullOrderingOfInterface()
{
// Arrange
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(IDisposable));
// Act
List<string> result = TemplateHelpers.GetViewNames(metadata, "UIHint", "DataType").ToList();
// Assert
Assert.Equal(4, result.Count);
Assert.Equal("UIHint", result[0]);
Assert.Equal("DataType", result[1]);
Assert.Equal("IDisposable", result[2]);
Assert.Equal("Object", result[3]);
}
[Fact]
public void GetViewNamesFullOrderingOfComplexTypeThatImplementsIEnumerable()
{
// Arrange
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(List<int>));
// Act
List<string> result = TemplateHelpers.GetViewNames(metadata, "UIHint", "DataType").ToList();
// Assert
Assert.Equal(5, result.Count);
Assert.Equal("UIHint", result[0]);
Assert.Equal("DataType", result[1]);
Assert.Equal("List`1", result[2]);
Assert.Equal("Collection", result[3]);
Assert.Equal("Object", result[4]);
}
[Fact]
public void GetViewNamesFullOrderingOfInterfaceThatRequiresIEnumerable()
{
// Arrange
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(IList<int>));
// Act
List<string> result = TemplateHelpers.GetViewNames(metadata, "UIHint", "DataType").ToList();
// Assert
Assert.Equal(5, result.Count);
Assert.Equal("UIHint", result[0]);
Assert.Equal("DataType", result[1]);
Assert.Equal("IList`1", result[2]);
Assert.Equal("Collection", result[3]);
Assert.Equal("Object", result[4]);
}
[Fact]
public void GetViewNamesNullUIHintNotIncludedInList()
{
// Arrange
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(Object));
// Act
List<string> result = TemplateHelpers.GetViewNames(metadata, null, "DataType").ToList();
// Assert
Assert.Equal(2, result.Count);
Assert.Equal("DataType", result[0]);
Assert.Equal("Object", result[1]);
}
[Fact]
public void GetViewNamesNullDataTypeNotIncludedInList()
{
// Arrange
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(Object));
// Act
List<string> result = TemplateHelpers.GetViewNames(metadata, "UIHint", null).ToList();
// Assert
Assert.Equal(2, result.Count);
Assert.Equal("UIHint", result[0]);
Assert.Equal("Object", result[1]);
}
[Fact]
public void GetViewNamesConvertsNullableOfTIntoT()
{
// Arrange
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(Nullable<int>));
// Act
List<string> result = TemplateHelpers.GetViewNames(metadata, null, null).ToList();
// Assert
Assert.Equal(2, result.Count);
Assert.Equal("Int32", result[0]);
Assert.Equal("String", result[1]);
}
// Template
private class TemplateModel
{
public object MyProperty { get; set; }
}
[Fact]
public void TemplateNullExpressionThrows()
{
// Arrange
HtmlHelper<object> html = MakeHtmlHelper<object>(null);
// Act & Assert
Assert.ThrowsArgumentNull(
() => TemplateHelpers.Template(html, null, "templateName", "htmlFieldName", DataBoundControlMode.ReadOnly,
null /* additionalViewData */, TemplateHelperSpy),
"expression");
}
[Fact]
public void TemplateDataNotFound()
{
// Arrange
HtmlHelper<object> html = MakeHtmlHelper<object>(null);
// Act
string result = TemplateHelpers.Template(html, "UnknownObject", "templateName", null, DataBoundControlMode.ReadOnly,
null /* additionalViewData */, TemplateHelperSpy);
// Assert
Assert.Equal("Model = (null), ModelType = System.String, RealModelType = System.String, PropertyName = (null), HtmlFieldName = UnknownObject, TemplateName = templateName, Mode = ReadOnly, AdditionalViewData = (null)", result);
}
[Fact]
public void TemplateHtmlFieldNameReplacesExpression()
{
// Arrange
HtmlHelper<object> html = MakeHtmlHelper<object>(null);
// Act
string result = TemplateHelpers.Template(html, "UnknownObject", "templateName", "htmlFieldName", DataBoundControlMode.ReadOnly,
new { foo = "Bar" }, TemplateHelperSpy);
// Assert
Assert.Equal("Model = (null), ModelType = System.String, RealModelType = System.String, PropertyName = (null), HtmlFieldName = htmlFieldName, TemplateName = templateName, Mode = ReadOnly, AdditionalViewData = { foo: Bar }", result);
}
[Fact]
public void TemplateDataFoundInViewDataDictionaryHasNoPropertyName()
{
// Arrange
HtmlHelper<object> html = MakeHtmlHelper<object>(null);
html.ViewContext.ViewData["Key"] = 42;
// Act
string result = TemplateHelpers.Template(html, "Key", null, null, DataBoundControlMode.Edit, null /* additionalViewData */, TemplateHelperSpy);
// Assert
Assert.Equal("Model = 42, ModelType = System.Int32, RealModelType = System.Int32, PropertyName = (null), HtmlFieldName = Key, TemplateName = (null), Mode = Edit, AdditionalViewData = (null)", result);
}
[Fact]
public void TemplateDataFoundInViewDataDictionarySubPropertyHasPropertyName()
{
// Arrange
HtmlHelper<object> html = MakeHtmlHelper<object>(null);
html.ViewContext.ViewData["Key"] = new TemplateModel { MyProperty = "Hello!" };
// Act
string result = TemplateHelpers.Template(html, "Key.MyProperty", null, null, DataBoundControlMode.ReadOnly,
null /* additionalViewData */, TemplateHelperSpy);
// Assert
Assert.Equal("Model = Hello!, ModelType = System.Object, RealModelType = System.String, PropertyName = MyProperty, HtmlFieldName = Key.MyProperty, TemplateName = (null), Mode = ReadOnly, AdditionalViewData = (null)", result);
}
[Fact]
public void TemplateDataFoundInModelHasPropertyName()
{
// Arrange
HtmlHelper<TemplateModel> html = MakeHtmlHelper<TemplateModel>(new TemplateModel { MyProperty = "Hello!" });
// Act
string result = TemplateHelpers.Template(html, "MyProperty", null, null, DataBoundControlMode.ReadOnly,
null /* additionalViewData */, TemplateHelperSpy);
// Assert
Assert.Equal("Model = Hello!, ModelType = System.Object, RealModelType = System.String, PropertyName = MyProperty, HtmlFieldName = MyProperty, TemplateName = (null), Mode = ReadOnly, AdditionalViewData = (null)", result);
}
[Fact]
public void TemplateNullDataFoundInModelHasPropertyTypeInsteadOfActualModelType()
{
// Arrange
HtmlHelper<TemplateModel> html = MakeHtmlHelper<TemplateModel>(new TemplateModel());
// Act
string result = TemplateHelpers.Template(html, "MyProperty", null, null, DataBoundControlMode.ReadOnly,
null /* additionalViewData */, TemplateHelperSpy);
// Assert
Assert.Equal("Model = (null), ModelType = System.Object, RealModelType = System.Object, PropertyName = MyProperty, HtmlFieldName = MyProperty, TemplateName = (null), Mode = ReadOnly, AdditionalViewData = (null)", result);
}
// TemplateFor
private class TemplateForModel
{
public int MyField = 0;
public object MyProperty { get; set; }
public Nullable<int> MyNullableProperty { get; set; }
}
[Fact]
public void TemplateForNonUnsupportedExpressionTypeThrows()
{
// Arrange
HtmlHelper<TemplateForModel> html = MakeHtmlHelper<TemplateForModel>(null);
// Act & Assert
Assert.Throws<InvalidOperationException>(
() => TemplateHelpers.TemplateFor(html, model => new Object(), "templateName", "htmlFieldName", DataBoundControlMode.ReadOnly,
null /* additionalViewData */, TemplateHelperSpy),
"Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.");
}
[Fact]
public void TemplateForWithNonNullExpression()
{
// Arrange
HtmlHelper<TemplateForModel> html = MakeHtmlHelper(new TemplateForModel { MyProperty = "Hello!" });
// Act
string result = TemplateHelpers.TemplateFor(html, model => model.MyProperty, "templateName", null, DataBoundControlMode.ReadOnly,
new { foo = "Bar" }, TemplateHelperSpy);
// Assert
Assert.Equal("Model = Hello!, ModelType = System.Object, RealModelType = System.String, PropertyName = MyProperty, HtmlFieldName = MyProperty, TemplateName = templateName, Mode = ReadOnly, AdditionalViewData = { foo: Bar }", result);
}
[Fact]
public void TemplateForHtmlFieldNameReplacesExpression()
{
// Arrange
HtmlHelper<TemplateForModel> html = MakeHtmlHelper(new TemplateForModel { MyProperty = "Hello!" });
// Act
string result = TemplateHelpers.TemplateFor(html, model => model.MyProperty, "templateName", "htmlFieldName",
DataBoundControlMode.ReadOnly, null /* additionalViewData */, TemplateHelperSpy);
// Assert
Assert.Equal("Model = Hello!, ModelType = System.Object, RealModelType = System.String, PropertyName = MyProperty, HtmlFieldName = htmlFieldName, TemplateName = templateName, Mode = ReadOnly, AdditionalViewData = (null)", result);
}
[Fact]
public void TemplateForNullModelStillRetainsTypeInformation()
{
// Arrange
HtmlHelper<TemplateForModel> html = MakeHtmlHelper<TemplateForModel>(null);
// Act
string result = TemplateHelpers.TemplateFor(html, model => model.MyProperty, null, null, DataBoundControlMode.ReadOnly,
null /* additionalViewData */, TemplateHelperSpy);
// Assert
Assert.Equal("Model = (null), ModelType = System.Object, RealModelType = System.Object, PropertyName = MyProperty, HtmlFieldName = MyProperty, TemplateName = (null), Mode = ReadOnly, AdditionalViewData = (null)", result);
}
[Fact]
public void TemplateForNullPropertyStillRetainsTypeInformation()
{
// Arrange
HtmlHelper<TemplateForModel> html = MakeHtmlHelper(new TemplateForModel());
// Act
string result = TemplateHelpers.TemplateFor(html, model => model.MyProperty, null, null, DataBoundControlMode.ReadOnly,
null /* additionalViewData */, TemplateHelperSpy);
// Assert
Assert.Equal("Model = (null), ModelType = System.Object, RealModelType = System.Object, PropertyName = MyProperty, HtmlFieldName = MyProperty, TemplateName = (null), Mode = ReadOnly, AdditionalViewData = (null)", result);
}
[Fact]
public void TemplateForNullableValueTYpePropertyRetainsNullableValueTYpeForNullPropertyValue()
{
// Arrange
HtmlHelper<TemplateForModel> html = MakeHtmlHelper(new TemplateForModel());
// Act
string result = TemplateHelpers.TemplateFor(html, model => model.MyNullableProperty, null, null, DataBoundControlMode.ReadOnly,
null /* additionalViewData */, TemplateHelperSpy);
// Assert
Assert.Equal("Model = (null), ModelType = System.Nullable`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], RealModelType = System.Nullable`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], PropertyName = MyNullableProperty, HtmlFieldName = MyNullableProperty, TemplateName = (null), Mode = ReadOnly, AdditionalViewData = (null)", result);
}
[Fact]
public void TemplateForNullableValueTypePropertyRetainsNullableValueTypeForNonNullPropertyValue()
{
// Arrange
HtmlHelper<TemplateForModel> html = MakeHtmlHelper(new TemplateForModel { MyNullableProperty = 42 });
// Act
string result = TemplateHelpers.TemplateFor(html, model => model.MyNullableProperty, null, null, DataBoundControlMode.ReadOnly,
null /* additionalViewData */, TemplateHelperSpy);
// Assert
Assert.Equal("Model = 42, ModelType = System.Nullable`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], RealModelType = System.Nullable`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], PropertyName = MyNullableProperty, HtmlFieldName = MyNullableProperty, TemplateName = (null), Mode = ReadOnly, AdditionalViewData = (null)", result);
}
[Fact]
public void TemplateForWithParameterExpression()
{
// Arrange
HtmlHelper<TemplateForModel> html = MakeHtmlHelper(new TemplateForModel());
// Act
string result = TemplateHelpers.TemplateFor(html, model => model, null, null, DataBoundControlMode.ReadOnly,
null /* additionalViewData */, TemplateHelperSpy);
// Assert
Assert.Equal("Model = System.Web.Mvc.Html.Test.TemplateHelpersTest+TemplateForModel, ModelType = System.Web.Mvc.Html.Test.TemplateHelpersTest+TemplateForModel, RealModelType = System.Web.Mvc.Html.Test.TemplateHelpersTest+TemplateForModel, PropertyName = (null), HtmlFieldName = (empty), TemplateName = (null), Mode = ReadOnly, AdditionalViewData = (null)", result);
}
[Fact]
public void TemplateForWithFieldExpression()
{
// Arrange
HtmlHelper<TemplateForModel> html = MakeHtmlHelper(new TemplateForModel { MyField = 42 });
// Act
string result = TemplateHelpers.TemplateFor(html, model => model.MyField, null, null, DataBoundControlMode.ReadOnly,
null /* additionalViewData */, TemplateHelperSpy);
// Assert
Assert.Equal("Model = 42, ModelType = System.Int32, RealModelType = System.Int32, PropertyName = (null), HtmlFieldName = MyField, TemplateName = (null), Mode = ReadOnly, AdditionalViewData = (null)", result);
}
// TemplateHelper
private class TemplateHelperModel
{
public string MyProperty { get; set; }
}
[Fact]
public void TemplateHelperNonNullNonEmptyStringModel()
{
// Arrange
HtmlHelper html = MakeHtmlHelper(new TemplateHelperModel { MyProperty = "Hello" });
ModelMetadata metadata = ModelMetadata.FromStringExpression("MyProperty", html.ViewData);
// Act
string result = TemplateHelpers.TemplateHelper(html, metadata, "htmlFieldName", "templateName", DataBoundControlMode.ReadOnly,
null /* additionalViewData */, ExecuteTemplateSpy);
// Assert
Assert.Equal("Model = Hello, ModelType = System.String, RealModelType = System.String, PropertyName = MyProperty, FormattedModelValue = Hello, HtmlFieldPrefix = FieldPrefix.htmlFieldName, TemplateName = templateName, Mode = ReadOnly", result);
}
[Fact]
public void TemplateHelperEmptyStringModel()
{
// Arrange
HtmlHelper html = MakeHtmlHelper(new TemplateHelperModel { MyProperty = "" });
ModelMetadata metadata = ModelMetadata.FromStringExpression("MyProperty", html.ViewData);
metadata.ConvertEmptyStringToNull = false;
// Act
string result = TemplateHelpers.TemplateHelper(html, metadata, "htmlFieldName", "templateName", DataBoundControlMode.ReadOnly,
null /* additionalViewData */, ExecuteTemplateSpy);
// Assert
Assert.Equal("Model = , ModelType = System.String, RealModelType = System.String, PropertyName = MyProperty, FormattedModelValue = , HtmlFieldPrefix = FieldPrefix.htmlFieldName, TemplateName = templateName, Mode = ReadOnly", result);
}
[Fact]
public void TemplateHelperConvertsEmptyStringsToNull()
{
// Arrange
HtmlHelper html = MakeHtmlHelper(new TemplateHelperModel { MyProperty = "" });
ModelMetadata metadata = ModelMetadata.FromStringExpression("MyProperty", html.ViewData);
metadata.ConvertEmptyStringToNull = true;
// Act
string result = TemplateHelpers.TemplateHelper(html, metadata, "htmlFieldName", "templateName", DataBoundControlMode.ReadOnly,
null /* additionalViewData */, ExecuteTemplateSpy);
// Assert
Assert.Equal("Model = (null), ModelType = System.String, RealModelType = System.String, PropertyName = MyProperty, FormattedModelValue = , HtmlFieldPrefix = FieldPrefix.htmlFieldName, TemplateName = templateName, Mode = ReadOnly", result);
}
[Fact]
public void TemplateHelperConvertsNullModelsToNullDisplayTextInReadOnlyMode()
{
// Arrange
HtmlHelper html = MakeHtmlHelper(new TemplateHelperModel());
ModelMetadata metadata = ModelMetadata.FromStringExpression("MyProperty", html.ViewData);
metadata.NullDisplayText = "NullDisplayText";
// Act
string result = TemplateHelpers.TemplateHelper(html, metadata, "htmlFieldName", "templateName", DataBoundControlMode.ReadOnly,
null /* additionalViewData */, ExecuteTemplateSpy);
// Assert
Assert.Equal("Model = (null), ModelType = System.String, RealModelType = System.String, PropertyName = MyProperty, FormattedModelValue = NullDisplayText, HtmlFieldPrefix = FieldPrefix.htmlFieldName, TemplateName = templateName, Mode = ReadOnly", result);
}
[Fact]
public void TemplateHelperDoesNotConvertNullModelsToNullDisplayTextInEditMode()
{
// Arrange
HtmlHelper html = MakeHtmlHelper(new TemplateHelperModel());
ModelMetadata metadata = ModelMetadata.FromStringExpression("MyProperty", html.ViewData);
metadata.NullDisplayText = "NullDisplayText";
// Act
string result = TemplateHelpers.TemplateHelper(html, metadata, "htmlFieldName", "templateName", DataBoundControlMode.Edit,
null /* additionalViewData */, ExecuteTemplateSpy);
// Assert
Assert.Equal("Model = (null), ModelType = System.String, RealModelType = System.String, PropertyName = MyProperty, FormattedModelValue = , HtmlFieldPrefix = FieldPrefix.htmlFieldName, TemplateName = templateName, Mode = Edit", result);
}
[Fact]
public void TemplateHelperAppliesDisplayFormatStringInReadOnlyMode()
{
// Arrange
HtmlHelper html = MakeHtmlHelper(new TemplateHelperModel { MyProperty = "Hello" });
ModelMetadata metadata = ModelMetadata.FromStringExpression("MyProperty", html.ViewData);
metadata.DisplayFormatString = "{0} world!";
// Act
string result = TemplateHelpers.TemplateHelper(html, metadata, "htmlFieldName", "templateName", DataBoundControlMode.ReadOnly,
null /* additionalViewData */, ExecuteTemplateSpy);
// Assert
Assert.Equal("Model = Hello, ModelType = System.String, RealModelType = System.String, PropertyName = MyProperty, FormattedModelValue = Hello world!, HtmlFieldPrefix = FieldPrefix.htmlFieldName, TemplateName = templateName, Mode = ReadOnly", result);
}
[Fact]
public void TemplateHelperDoesNotApplyDisplayFormatStringInReadOnlyModeForNullModel()
{
// Arrange
HtmlHelper html = MakeHtmlHelper(new TemplateHelperModel());
ModelMetadata metadata = ModelMetadata.FromStringExpression("MyProperty", html.ViewData);
metadata.DisplayFormatString = "{0} world!";
// Act
string result = TemplateHelpers.TemplateHelper(html, metadata, "htmlFieldName", "templateName", DataBoundControlMode.ReadOnly,
null /* additionalViewData */, ExecuteTemplateSpy);
// Assert
Assert.Equal("Model = (null), ModelType = System.String, RealModelType = System.String, PropertyName = MyProperty, FormattedModelValue = , HtmlFieldPrefix = FieldPrefix.htmlFieldName, TemplateName = templateName, Mode = ReadOnly", result);
}
[Fact]
public void TemplateHelperAppliesEditFormatStringInEditMode()
{
HtmlHelper html = MakeHtmlHelper(new TemplateHelperModel { MyProperty = "Hello" });
ModelMetadata metadata = ModelMetadata.FromStringExpression("MyProperty", html.ViewData);
metadata.EditFormatString = "{0} world!";
// Act
string result = TemplateHelpers.TemplateHelper(html, metadata, "htmlFieldName", "templateName", DataBoundControlMode.Edit,
null /* additionalViewData */, ExecuteTemplateSpy);
// Assert
Assert.Equal("Model = Hello, ModelType = System.String, RealModelType = System.String, PropertyName = MyProperty, FormattedModelValue = Hello world!, HtmlFieldPrefix = FieldPrefix.htmlFieldName, TemplateName = templateName, Mode = Edit", result);
}
[Fact]
public void TemplateHelperDoesNotApplyEditFormatStringInEditModeForNullModel()
{
// Arrange
HtmlHelper html = MakeHtmlHelper(new TemplateHelperModel());
ModelMetadata metadata = ModelMetadata.FromStringExpression("MyProperty", html.ViewData);
metadata.EditFormatString = "{0} world!";
// Act
string result = TemplateHelpers.TemplateHelper(html, metadata, "htmlFieldName", "templateName", DataBoundControlMode.Edit,
null /* additionalViewData */, ExecuteTemplateSpy);
// Assert
Assert.Equal("Model = (null), ModelType = System.String, RealModelType = System.String, PropertyName = MyProperty, FormattedModelValue = , HtmlFieldPrefix = FieldPrefix.htmlFieldName, TemplateName = templateName, Mode = Edit", result);
}
[Fact]
public void TemplateHelperAddsNonNullModelToVisitedObjects()
{ // DDB #224750
HtmlHelper html = MakeHtmlHelper(new TemplateHelperModel { MyProperty = "Hello" });
ModelMetadata metadata = ModelMetadata.FromStringExpression("MyProperty", html.ViewData);
ViewDataDictionary viewData = null;
// Act
TemplateHelpers.TemplateHelper(
html, metadata, "htmlFieldName", "templateName", DataBoundControlMode.ReadOnly, null /* additionalViewData */,
(_html, _viewData, _templateName, _mode, _getViews, _getDefaultActions) =>
{
viewData = _viewData;
return String.Empty;
});
// Assert
Assert.NotNull(viewData);
Assert.True(viewData.TemplateInfo.VisitedObjects.Contains("Hello"));
}
[Fact]
public void TemplateHelperAddsNullModelsTypeToVisitedObjects()
{ // DDB #224750
HtmlHelper html = MakeHtmlHelper(new TemplateHelperModel());
ModelMetadata metadata = ModelMetadata.FromStringExpression("MyProperty", html.ViewData);
ViewDataDictionary viewData = null;
// Act
TemplateHelpers.TemplateHelper(
html, metadata, "htmlFieldName", "templateName", DataBoundControlMode.ReadOnly, null /* additionalViewData */,
(_html, _viewData, _templateName, _mode, _getViews, _getDefaultActions) =>
{
viewData = _viewData;
return String.Empty;
});
// Assert
Assert.NotNull(viewData);
Assert.True(viewData.TemplateInfo.VisitedObjects.Contains(typeof(string)));
}
[Fact]
public void TemplateHelperReturnsEmptyStringForAlreadyVisitedObject()
{ // DDB #224750
HtmlHelper html = MakeHtmlHelper(new TemplateHelperModel { MyProperty = "Hello" });
ModelMetadata metadata = ModelMetadata.FromStringExpression("MyProperty", html.ViewData);
html.ViewData.TemplateInfo.VisitedObjects.Add("Hello");
// Act
string result = TemplateHelpers.TemplateHelper(html, metadata, "htmlFieldName", "templateName", DataBoundControlMode.ReadOnly,
null /* additionalViewData */);
// Assert
Assert.Equal(String.Empty, result);
}
[Fact]
public void TemplateHelperReturnsEmptyStringForAlreadyVisitedType()
{ // DDB #224750
HtmlHelper html = MakeHtmlHelper(new TemplateHelperModel());
ModelMetadata metadata = ModelMetadata.FromStringExpression("MyProperty", html.ViewData);
html.ViewData.TemplateInfo.VisitedObjects.Add(typeof(string));
// Act
string result = TemplateHelpers.TemplateHelper(html, metadata, "htmlFieldName", "templateName", DataBoundControlMode.ReadOnly,
null /* additionalViewData */);
// Assert
Assert.Equal(String.Empty, result);
}
[Fact]
public void TemplateHelperPreservesSameInstanceOfModelMetadata()
{ // DDB #225858
HtmlHelper html = MakeHtmlHelper(new TemplateHelperModel());
ModelMetadata metadata = ModelMetadata.FromStringExpression("MyProperty", html.ViewData);
ViewDataDictionary callbackViewData = null;
// Act
string result = TemplateHelpers.TemplateHelper(html, metadata, "htmlFieldName", "templateName", DataBoundControlMode.ReadOnly,
null /* additionalViewData */,
(_html, _viewData, _templateName, _mode, _getViewNames, _getDefaultActions) =>
{
callbackViewData = _viewData;
return String.Empty;
});
// Assert
Assert.NotNull(callbackViewData);
Assert.Same(metadata, callbackViewData.ModelMetadata);
}
[Fact]
public void TemplateHelperFormatsValuesUsingCurrentCulture()
{
CultureInfo existingCulture = Thread.CurrentThread.CurrentCulture;
try
{
// Arrange
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("es-PR");
HtmlHelper html = MakeHtmlHelper(new { MyProperty = new DateTime(2009, 11, 18, 16, 12, 8, DateTimeKind.Utc) });
ModelMetadata metadata = ModelMetadata.FromStringExpression("MyProperty", html.ViewData);
metadata.DisplayFormatString = "{0:F}";
// Act
string result = TemplateHelpers.TemplateHelper(html, metadata, "htmlFieldName", "templateName", DataBoundControlMode.ReadOnly,
null /* additionalViewData */, ExecuteTemplateSpy);
// Assert
Assert.Equal("Model = 18/11/2009 04:12:08 p.m., ModelType = System.DateTime, RealModelType = System.DateTime, PropertyName = MyProperty, FormattedModelValue = miércoles, 18 de noviembre de 2009 04:12:08 p.m., HtmlFieldPrefix = FieldPrefix.htmlFieldName, TemplateName = templateName, Mode = ReadOnly", result);
}
finally
{
Thread.CurrentThread.CurrentCulture = existingCulture;
}
}
[Fact]
public void TemplateHelperPreservesExistingViewData()
{
HtmlHelper html = MakeHtmlHelper(new TemplateHelperModel());
html.ViewData["Foo"] = "Bar";
html.ViewData["Baz"] = 42;
ModelMetadata metadata = ModelMetadata.FromStringExpression("MyProperty", html.ViewData);
ViewDataDictionary callbackViewData = null;
// Act
string result = TemplateHelpers.TemplateHelper(html, metadata, "htmlFieldName", "templateName", DataBoundControlMode.ReadOnly,
null /* additionalViewData */,
(_html, _viewData, _templateName, _mode, _getViewNames, _getDefaultActions) =>
{
callbackViewData = _viewData;
return String.Empty;
});
// Assert
Assert.NotSame(html.ViewData, callbackViewData);
Assert.Equal(2, callbackViewData.Count);
Assert.Equal("Bar", callbackViewData["Foo"]);
Assert.Equal(42, callbackViewData["Baz"]);
}
[Fact]
public void TemplateHelperMergesAdditionalViewData()
{
HtmlHelper html = MakeHtmlHelper(new TemplateHelperModel());
html.ViewData["Foo"] = "Bar";
html.ViewData["Baz"] = 42;
ModelMetadata metadata = ModelMetadata.FromStringExpression("MyProperty", html.ViewData);
ViewDataDictionary callbackViewData = null;
// Act
string result = TemplateHelpers.TemplateHelper(html, metadata, "htmlFieldName", "templateName", DataBoundControlMode.ReadOnly,
new { foo = "New Foo", hello = "World!" },
(_html, _viewData, _templateName, _mode, _getViewNames, _getDefaultActions) =>
{
callbackViewData = _viewData;
return String.Empty;
});
// Assert
Assert.NotSame(html.ViewData, callbackViewData);
Assert.Equal(3, callbackViewData.Count);
Assert.Equal("New Foo", callbackViewData["Foo"]);
Assert.Equal(42, callbackViewData["Baz"]);
Assert.Equal("World!", callbackViewData["Hello"]);
}
// Helpers
private static string ExecuteTemplateSpy(HtmlHelper html, ViewDataDictionary viewData, string templateName, DataBoundControlMode mode,
TemplateHelpers.GetViewNamesDelegate getViewNames, TemplateHelpers.GetDefaultActionsDelegate getDefaultActions)
{
Assert.Same(viewData.Model, viewData.ModelMetadata.Model);
Assert.Equal<TemplateHelpers.GetViewNamesDelegate>(TemplateHelpers.GetViewNames, getViewNames);
Assert.Equal<TemplateHelpers.GetDefaultActionsDelegate>(TemplateHelpers.GetDefaultActions, getDefaultActions);
return String.Format("Model = {0}, ModelType = {1}, RealModelType = {2}, PropertyName = {3}, FormattedModelValue = {4}, HtmlFieldPrefix = {5}, TemplateName = {6}, Mode = {7}",
viewData.ModelMetadata.Model ?? "(null)",
viewData.ModelMetadata.ModelType == null ? "(null)" : viewData.ModelMetadata.ModelType.FullName,
viewData.ModelMetadata.RealModelType == null ? "(null)" : viewData.ModelMetadata.RealModelType.FullName,
viewData.ModelMetadata.PropertyName ?? "(null)",
viewData.TemplateInfo.FormattedModelValue ?? "(null)",
viewData.TemplateInfo.HtmlFieldPrefix == "" ? "(empty)" : viewData.TemplateInfo.HtmlFieldPrefix ?? "(null)",
templateName ?? "(null)",
mode);
}
private static string TemplateHelperSpy(HtmlHelper html, ModelMetadata metadata, string htmlFieldName, string templateName, DataBoundControlMode mode,
object additionalViewData)
{
return String.Format("Model = {0}, ModelType = {1}, RealModelType = {2}, PropertyName = {3}, HtmlFieldName = {4}, TemplateName = {5}, Mode = {6}, AdditionalViewData = {7}",
metadata.Model ?? "(null)",
metadata.ModelType == null ? "(null)" : metadata.ModelType.FullName,
metadata.RealModelType == null ? "(null)" : metadata.RealModelType.FullName,
metadata.PropertyName ?? "(null)",
htmlFieldName == String.Empty ? "(empty)" : htmlFieldName ?? "(null)",
templateName ?? "(null)",
mode,
AnonymousObject.Inspect(additionalViewData));
}
private HtmlHelper<TModel> MakeHtmlHelper<TModel>(TModel model)
{
return MakeHtmlHelper<TModel>(model, model);
}
private HtmlHelper<TModel> MakeHtmlHelper<TModel>(TModel 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<HttpContextBase> httpContext = new Mock<HttpContextBase>();
httpContext.Setup(c => c.Items).Returns(new Hashtable());
Mock<ViewContext> viewContext = new Mock<ViewContext> { CallBase = true };
viewContext.Setup(c => c.View).Returns(new DummyView());
viewContext.Setup(c => c.ViewData).Returns(viewData);
viewContext.Setup(c => c.HttpContext).Returns(httpContext.Object);
viewContext.Setup(c => c.RouteData).Returns(new RouteData());
viewContext.Setup(c => c.TempData).Returns(new TempDataDictionary());
viewContext.Setup(c => c.Writer).Returns(TextWriter.Null);
return new HtmlHelper<TModel>(viewContext.Object, new SimpleViewDataContainer(viewData));
}
private ViewDataDictionary MakeViewData(HtmlHelper html, ModelMetadata metadata)
{
return new ViewDataDictionary(html.ViewDataContainer.ViewData)
{
Model = metadata.Model,
ModelMetadata = metadata,
TemplateInfo = new TemplateInfo
{
FormattedModelValue = metadata.Model,
HtmlFieldPrefix = "FieldPrefix",
}
};
}
private class DummyView : IView
{
public void Render(ViewContext viewContext, TextWriter writer)
{
throw new NotImplementedException();
}
}
private class MockViewEngine : IDisposable
{
List<IViewEngine> oldEngines;
public MockViewEngine(bool returnView = true)
{
oldEngines = ViewEngines.Engines.ToList();
View = new Mock<IView>();
Engine = new Mock<IViewEngine>();
Engine.Setup(e => e.FindPartialView(It.IsAny<ControllerContext>(), It.IsAny<string>(), It.IsAny<bool>()))
.Returns(returnView ? new ViewEngineResult(View.Object, Engine.Object) : new ViewEngineResult(new string[0]));
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(Engine.Object);
}
public void Dispose()
{
ViewEngines.Engines.Clear();
foreach (IViewEngine engine in oldEngines)
{
ViewEngines.Engines.Add(engine);
}
}
public Mock<IViewEngine> Engine { get; set; }
public Mock<IView> View { get; set; }
}
}
}