diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultDisplayTemplatesTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultDisplayTemplatesTests.cs index f9ab3f19d8..cb7155f3ce 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultDisplayTemplatesTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultDisplayTemplatesTests.cs @@ -97,6 +97,68 @@ namespace Microsoft.AspNet.Mvc.Core Assert.Equal(expected, result); } + [Fact] + public void ObjectTemplate_HonoursHideSurroundingHtml() + { + // Arrange + var expected = + "Model = p1, ModelType = System.String, PropertyName = Property1, SimpleDisplayText = p1" + + "
Property2
" + Environment.NewLine + + "
Model = (null), ModelType = System.String, PropertyName = Property2," + + " SimpleDisplayText = (null)
" + Environment.NewLine; + + var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "p1", Property2 = null }; + var html = DefaultTemplatesUtilities.GetHtmlHelper(model); + var metadata = + html.ViewData.ModelMetadata.Properties.First(m => string.Equals(m.PropertyName, "Property1")); + metadata.HideSurroundingHtml = true; + + // Act + var result = DefaultDisplayTemplates.ObjectTemplate(html); + + // Assert + Assert.Equal(expected, result); + } + + [Fact] + public void HiddenInputTemplate_ReturnsValue() + { + // Arrange + var model = "Model string"; + var html = DefaultTemplatesUtilities.GetHtmlHelper(model); + var templateInfo = html.ViewData.TemplateInfo; + templateInfo.HtmlFieldPrefix = "FieldPrefix"; + + // TemplateBuilder sets FormattedModelValue before calling TemplateRenderer and it's used below. + templateInfo.FormattedModelValue = "Formatted string"; + + // Act + var result = DefaultDisplayTemplates.HiddenInputTemplate(html); + + // Assert + Assert.Equal("Formatted string", result); + } + + [Fact] + public void HiddenInputTemplate_HonoursHideSurroundingHtml() + { + // Arrange + var model = "Model string"; + var html = DefaultTemplatesUtilities.GetHtmlHelper(model); + var viewData = html.ViewData; + viewData.ModelMetadata.HideSurroundingHtml = true; + + var templateInfo = viewData.TemplateInfo; + templateInfo.HtmlFieldPrefix = "FieldPrefix"; + templateInfo.FormattedModelValue = "Formatted string"; + + // Act + var result = DefaultDisplayTemplates.HiddenInputTemplate(html); + + // Assert + Assert.Empty(result); + } + [Fact] public void Display_FindsViewDataMember() { diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultEditorTemplatesTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultEditorTemplatesTests.cs index 7996a4af89..e18f9012d3 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultEditorTemplatesTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultEditorTemplatesTests.cs @@ -111,6 +111,77 @@ Environment.NewLine; Assert.Equal(expected, result); } + [Fact] + public void ObjectTemplate_HonoursHideSurroundingHtml() + { + // Arrange + var expected = + "Model = p1, ModelType = System.String, PropertyName = Property1, SimpleDisplayText = p1" + + "
" + + Environment.NewLine + + "
" + + "Model = (null), ModelType = System.String, PropertyName = Property2, SimpleDisplayText = (null) " + + "" + + "
" + + Environment.NewLine; + + var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "p1", Property2 = null }; + var html = DefaultTemplatesUtilities.GetHtmlHelper(model); + var metadata = + html.ViewData.ModelMetadata.Properties.First(m => string.Equals(m.PropertyName, "Property1")); + metadata.HideSurroundingHtml = true; + + // Act + var result = DefaultEditorTemplates.ObjectTemplate(html); + + // Assert + Assert.Equal(expected, result); + } + + [Fact] + public void HiddenInputTemplate_ReturnsValueAndHiddenInput() + { + // Arrange + var expected = + "Formatted string"; + + var model = "Model string"; + var html = DefaultTemplatesUtilities.GetHtmlHelper(model); + var templateInfo = html.ViewData.TemplateInfo; + templateInfo.HtmlFieldPrefix = "FieldPrefix"; + + // TemplateBuilder sets FormattedModelValue before calling TemplateRenderer and it's used below. + templateInfo.FormattedModelValue = "Formatted string"; + + // Act + var result = DefaultEditorTemplates.HiddenInputTemplate(html); + + // Assert + Assert.Equal(expected, result); + } + + [Fact] + public void HiddenInputTemplate_HonoursHideSurroundingHtml() + { + // Arrange + var expected = ""; + + var model = "Model string"; + var html = DefaultTemplatesUtilities.GetHtmlHelper(model); + var viewData = html.ViewData; + viewData.ModelMetadata.HideSurroundingHtml = true; + + var templateInfo = viewData.TemplateInfo; + templateInfo.HtmlFieldPrefix = "FieldPrefix"; + templateInfo.FormattedModelValue = "Formatted string"; + + // Act + var result = DefaultEditorTemplates.HiddenInputTemplate(html); + + // Assert + Assert.Equal(expected, result); + } + [Fact] public void Editor_FindsViewDataMember() { diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/CachedDataAnnotationsMetadataAttributesTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/CachedDataAnnotationsMetadataAttributesTest.cs index 571d99ef0e..9c65f93877 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/CachedDataAnnotationsMetadataAttributesTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/CachedDataAnnotationsMetadataAttributesTest.cs @@ -27,6 +27,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding Assert.Null(cache.DisplayColumn); Assert.Null(cache.DisplayFormat); Assert.Null(cache.Editable); + Assert.Null(cache.HiddenInput); Assert.Null(cache.Required); } @@ -37,26 +38,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding { return new TheoryData> { - { - new DisplayAttribute(), - (CachedDataAnnotationsMetadataAttributes cache) => cache.Display - }, - { - new DisplayColumnAttribute("Property"), - (CachedDataAnnotationsMetadataAttributes cache) => cache.DisplayColumn - }, - { - new DisplayFormatAttribute(), - (CachedDataAnnotationsMetadataAttributes cache) => cache.DisplayFormat - }, - { - new EditableAttribute(allowEdit: false), - (CachedDataAnnotationsMetadataAttributes cache) => cache.Editable - }, - { - new RequiredAttribute(), - (CachedDataAnnotationsMetadataAttributes cache) => cache.Required - }, + { new DisplayAttribute(), cache => cache.Display }, + { new DisplayColumnAttribute("Property"), cache => cache.DisplayColumn }, + { new DisplayFormatAttribute(), cache => cache.DisplayFormat }, + { new EditableAttribute(allowEdit: false), cache => cache.Editable }, + { new HiddenInputAttribute(), cache => cache.HiddenInput }, + { new RequiredAttribute(), cache => cache.Required }, }; } } diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/CachedDataAnnotationsModelMetadataTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/CachedDataAnnotationsModelMetadataTest.cs index 3af0eefeec..df28e580e7 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/CachedDataAnnotationsModelMetadataTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/CachedDataAnnotationsModelMetadataTest.cs @@ -30,12 +30,23 @@ namespace Microsoft.AspNet.Mvc.ModelBinding // Assert Assert.True(metadata.ConvertEmptyStringToNull); + Assert.False(metadata.HideSurroundingHtml); + Assert.True(metadata.IsComplexType); Assert.False(metadata.IsReadOnly); Assert.False(metadata.IsRequired); + Assert.True(metadata.ShowForDisplay); + Assert.True(metadata.ShowForEdit); + Assert.Null(metadata.DataTypeName); Assert.Null(metadata.Description); + Assert.Null(metadata.DisplayFormatString); Assert.Null(metadata.DisplayName); + Assert.Null(metadata.EditFormatString); Assert.Null(metadata.NullDisplayText); + Assert.Null(metadata.SimpleDisplayText); + Assert.Null(metadata.TemplateHint); + + Assert.Equal(ModelMetadata.DefaultOrder, metadata.Order); } public static TheoryData> ExpectedAttributeDataStrings @@ -45,20 +56,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding return new TheoryData> { { - new DisplayAttribute { Description = "value" }, - (ModelMetadata metadata) => metadata.Description + new DisplayAttribute { Description = "value" }, metadata => metadata.Description }, { - new DisplayAttribute { Name = "value" }, - (ModelMetadata metadata) => metadata.DisplayName + new DisplayAttribute { Name = "value" }, metadata => metadata.DisplayName }, { - new DisplayColumnAttribute("Property"), - (ModelMetadata metadata) => metadata.SimpleDisplayText + new DisplayColumnAttribute("Property"), metadata => metadata.SimpleDisplayText }, { - new DisplayFormatAttribute { NullDisplayText = "value" }, - (ModelMetadata metadata) => metadata.NullDisplayText + new DisplayFormatAttribute { NullDisplayText = "value" }, metadata => metadata.NullDisplayText }, }; } @@ -71,8 +78,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding // Arrange var attributes = new[] { attribute }; var provider = new DataAnnotationsModelMetadataProvider(); - - // Act var metadata = new CachedDataAnnotationsModelMetadata( provider, containerType: null, @@ -82,6 +87,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding { Model = new ClassWithDisplayableColumn { Property = "value" }, }; + + // Act var result = accessor(metadata); // Assert @@ -96,27 +103,37 @@ namespace Microsoft.AspNet.Mvc.ModelBinding { { new DisplayFormatAttribute { ConvertEmptyStringToNull = false }, - (ModelMetadata metadata) => metadata.ConvertEmptyStringToNull, + metadata => metadata.ConvertEmptyStringToNull, false }, { new DisplayFormatAttribute { ConvertEmptyStringToNull = true }, - (ModelMetadata metadata) => metadata.ConvertEmptyStringToNull, + metadata => metadata.ConvertEmptyStringToNull, true }, { new EditableAttribute(allowEdit: false), - (ModelMetadata metadata) => metadata.IsReadOnly, + metadata => metadata.IsReadOnly, true }, { new EditableAttribute(allowEdit: true), - (ModelMetadata metadata) => metadata.IsReadOnly, + metadata => metadata.IsReadOnly, + false + }, + { + new HiddenInputAttribute { DisplayValue = false }, + metadata => metadata.HideSurroundingHtml, + true + }, + { + new HiddenInputAttribute { DisplayValue = true }, + metadata => metadata.HideSurroundingHtml, false }, { new RequiredAttribute(), - (ModelMetadata metadata) => metadata.IsRequired, + metadata => metadata.IsRequired, true }, }; @@ -133,23 +150,67 @@ namespace Microsoft.AspNet.Mvc.ModelBinding // Arrange var attributes = new[] { attribute }; var provider = new DataAnnotationsModelMetadataProvider(); - - // Act var metadata = new CachedDataAnnotationsModelMetadata( provider, containerType: null, modelType: typeof(object), propertyName: null, attributes: attributes); + + // Act var result = accessor(metadata); // Assert Assert.Equal(expectedResult, result); } + [Fact] + public void HiddenInputWorksOnProperty() + { + // Arrange + var provider = new DataAnnotationsModelMetadataProvider(); + var metadata = provider.GetMetadataForType(modelAccessor: null, modelType: typeof(ClassWithHiddenProperties)); + var property = metadata.Properties.First(m => string.Equals("DirectlyHidden", m.PropertyName)); + + // Act + var result = property.HideSurroundingHtml; + + // Assert + Assert.True(result); + } + + // TODO #1000; enable test once we detect attributes on the property's type + public void HiddenInputWorksOnPropertyType() + { + // Arrange + var provider = new DataAnnotationsModelMetadataProvider(); + var metadata = provider.GetMetadataForType(modelAccessor: null, modelType: typeof(ClassWithHiddenProperties)); + var property = metadata.Properties.First(m => string.Equals("OfHiddenType", m.PropertyName)); + + // Act + var result = property.HideSurroundingHtml; + + // Assert + Assert.True(result); + } + private class ClassWithDisplayableColumn { public string Property { get; set; } } + + [HiddenInput(DisplayValue = false)] + private class HiddenClass + { + public string Property { get; set; } + } + + private class ClassWithHiddenProperties + { + [HiddenInput(DisplayValue = false)] + public string DirectlyHidden { get; set; } + + public HiddenClass OfHiddenType { get; set; } + } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/ModelMetadataTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/ModelMetadataTest.cs index 3daac32738..25ea719d90 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/ModelMetadataTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/ModelMetadataTest.cs @@ -13,6 +13,33 @@ namespace Microsoft.AspNet.Mvc.ModelBinding { public class ModelMetadataTest { + public static TheoryData, Func, object> MetadataModifierData + { + get + { + return new TheoryData, Func, object> + { + { m => m.ConvertEmptyStringToNull = false, m => m.ConvertEmptyStringToNull, false }, + { m => m.HideSurroundingHtml = true, m => m.HideSurroundingHtml, true }, + { m => m.IsReadOnly = true, m => m.IsReadOnly, true }, + { m => m.IsRequired = true, m => m.IsRequired, true }, + { m => m.ShowForDisplay = false, m => m.ShowForDisplay, false }, + { m => m.ShowForEdit = false, m => m.ShowForEdit, false }, + + { m => m.DataTypeName = "New data type name", m => m.DataTypeName, "New data type name" }, + { m => m.Description = "New description", m => m.Description, "New description" }, + { m => m.DisplayFormatString = "New display format", m => m.DisplayFormatString, "New display format" }, + { m => m.DisplayName = "New display name", m => m.DisplayName, "New display name" }, + { m => m.EditFormatString = "New edit format", m => m.EditFormatString, "New edit format" }, + { m => m.NullDisplayText = "New null display", m => m.NullDisplayText, "New null display" }, + { m => m.SimpleDisplayText = "New simple display", m => m.SimpleDisplayText, "New simple display" }, + { m => m.TemplateHint = "New template hint", m => m.TemplateHint, "New template hint" }, + + { m => m.Order = 23, m => m.Order, 23 }, + }; + } + } + #if NET45 // Constructor @@ -27,11 +54,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding // Assert Assert.Equal(typeof(Exception), metadata.ContainerType); + Assert.True(metadata.ConvertEmptyStringToNull); + Assert.False(metadata.HideSurroundingHtml); Assert.False(metadata.IsComplexType); + Assert.False(metadata.IsNullableValueType); Assert.False(metadata.IsReadOnly); Assert.False(metadata.IsRequired); + Assert.True(metadata.ShowForDisplay); + Assert.True(metadata.ShowForEdit); + Assert.Null(metadata.DataTypeName); Assert.Null(metadata.Description); Assert.Null(metadata.DisplayFormatString); Assert.Null(metadata.DisplayName); @@ -41,8 +74,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding Assert.Equal("model", metadata.Model); Assert.Equal("model", metadata.SimpleDisplayText); + Assert.Equal(typeof(string), metadata.RealModelType); Assert.Equal(typeof(string), metadata.ModelType); Assert.Equal("propertyName", metadata.PropertyName); + + Assert.Equal(ModelMetadata.DefaultOrder, metadata.Order); } #endif @@ -188,6 +224,78 @@ namespace Microsoft.AspNet.Mvc.ModelBinding Assert.Equal("Prop2", newProp.PropertyName); } + [Fact] + public void PropertiesSetOnce() + { + // Arrange + var provider = new EmptyModelMetadataProvider(); + var metadata = new ModelMetadata( + provider, + containerType: null, + modelAccessor: () => new Class1(), + modelType: typeof(Class1), + propertyName: null); + + // Act + var firstPropertiesEvaluation = metadata.Properties; + var secondPropertiesEvaluation = metadata.Properties; + + // Assert + // Same IEnumerable object. + Assert.Same(firstPropertiesEvaluation, secondPropertiesEvaluation); + } + + [Fact] + public void PropertiesEnumerationEvaluatedOnce() + { + // Arrange + var provider = new EmptyModelMetadataProvider(); + var metadata = new ModelMetadata( + provider, + containerType: null, + modelAccessor: () => new Class1(), + modelType: typeof(Class1), + propertyName: null); + + // Act + var firstPropertiesEvaluation = metadata.Properties.ToList(); + var secondPropertiesEvaluation = metadata.Properties.ToList(); + + // Assert + // Identical ModelMetadata objects every time we run through the Properties collection. + Assert.Equal(firstPropertiesEvaluation, secondPropertiesEvaluation); + } + + [Theory] + [MemberData("MetadataModifierData")] + public void PropertiesPropertyChangesPersist( + Action setter, + Func getter, + object expected) + { + // Arrange + var provider = new EmptyModelMetadataProvider(); + var metadata = new ModelMetadata( + provider, + containerType: null, + modelAccessor: () => new Class1(), + modelType: typeof(Class1), + propertyName: null); + + // Act + foreach (var property in metadata.Properties) + { + setter(property); + } + + // Assert + foreach (var property in metadata.Properties) + { + // Due to boxing of structs, can't Assert.Same(). + Assert.Equal(expected, getter(property)); + } + } + private class Class1 { public string Prop1 { get; set; } @@ -315,6 +423,31 @@ namespace Microsoft.AspNet.Mvc.ModelBinding public Class1 Prop1 { get; set; } } + [Theory] + [MemberData("MetadataModifierData")] + public void PropertyChangesPersist( + Action setter, + Func getter, + object expected) + { + // Arrange + var provider = new EmptyModelMetadataProvider(); + var metadata = new ModelMetadata( + provider, + containerType: null, + modelAccessor: () => new Class1(), + modelType: typeof(Class1), + propertyName: null); + + // Act + setter(metadata); + var result = getter(metadata); + + // Assert + // Due to boxing of structs, can't Assert.Same(). + Assert.Equal(expected, result); + } + // Helpers private class DummyContactModel