Add `[HiddenInput]`, `ModelMetadata.HideSurroundingHtml`, and `.Properties` tests

nits:
- add a few missing default `ModelMetadata` property value checks
- cleanup some redundancies in test data initializers
This commit is contained in:
dougbu 2014-08-14 07:54:43 -07:00
parent c0179f74cc
commit 1a4bd25e0f
5 changed files with 351 additions and 37 deletions

View File

@ -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" +
"<div class=\"display-label\">Property2</div>" + Environment.NewLine +
"<div class=\"display-field\">Model = (null), ModelType = System.String, PropertyName = Property2," +
" SimpleDisplayText = (null)</div>" + 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()
{

View File

@ -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" +
"<div class=\"editor-label\"><label for=\"Property2\">Property2</label></div>" +
Environment.NewLine +
"<div class=\"editor-field\">" +
"Model = (null), ModelType = System.String, PropertyName = Property2, SimpleDisplayText = (null) " +
"<span class=\"field-validation-valid\" data-valmsg-for=\"Property2\" data-valmsg-replace=\"true\">" +
"</span></div>" +
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<input id=\"FieldPrefix\" name=\"FieldPrefix\" type=\"hidden\" value=\"Model 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 = "<input id=\"FieldPrefix\" name=\"FieldPrefix\" type=\"hidden\" value=\"Model string\" />";
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()
{

View File

@ -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<Attribute, Func<CachedDataAnnotationsMetadataAttributes, Attribute>>
{
{
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 },
};
}
}

View File

@ -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<Attribute, Func<ModelMetadata, string>> ExpectedAttributeDataStrings
@ -45,20 +56,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return new TheoryData<Attribute, Func<ModelMetadata, string>>
{
{
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; }
}
}
}

View File

@ -13,6 +13,33 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class ModelMetadataTest
{
public static TheoryData<Action<ModelMetadata>, Func<ModelMetadata, object>, object> MetadataModifierData
{
get
{
return new TheoryData<Action<ModelMetadata>, Func<ModelMetadata, object>, 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<ModelMetadata> 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<ModelMetadata> setter,
Func<ModelMetadata, object> 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<ModelMetadata> setter,
Func<ModelMetadata, object> 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