diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Microsoft.AspNet.Mvc.Core.Test.kproj b/test/Microsoft.AspNet.Mvc.Core.Test/Microsoft.AspNet.Mvc.Core.Test.kproj index fd81abcdf4..169e49f440 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Microsoft.AspNet.Mvc.Core.Test.kproj +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Microsoft.AspNet.Mvc.Core.Test.kproj @@ -70,7 +70,10 @@ + + + diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/HtmlHelperDisplayNameExtensionsTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/HtmlHelperDisplayNameExtensionsTest.cs new file mode 100644 index 0000000000..783cc39480 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/HtmlHelperDisplayNameExtensionsTest.cs @@ -0,0 +1,326 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using Microsoft.AspNet.Mvc.ModelBinding; +using Microsoft.AspNet.Mvc.Rendering; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Mvc.Core +{ + /// + /// Test the class. + /// + public class HtmlHelperDisplayNameExtensionsTest + { + [Fact] + public void DisplayNameHelpers_ReturnEmptyForModel() + { + // Arrange + var helper = DefaultTemplatesUtilities.GetHtmlHelper(); + var enumerableHelper = + DefaultTemplatesUtilities.GetHtmlHelper>(null); + + // Act + var displayNameResult = helper.DisplayName(""); + var displayNameNullResult = helper.DisplayName(expression: null); // null is another alias for current model + var displayNameForResult = helper.DisplayNameFor(m => m); + var displayNameForEnumerableModelResult = enumerableHelper.DisplayNameFor(m => m); + var displayNameForModelResult = helper.DisplayNameForModel(); + + // Assert + Assert.Empty(displayNameResult.ToString()); + Assert.Empty(displayNameNullResult.ToString()); + Assert.Empty(displayNameForResult.ToString()); + Assert.Empty(displayNameForEnumerableModelResult.ToString()); + Assert.Empty(displayNameForModelResult.ToString()); + } + + [Fact] + public void DisplayNameHelpers_ReturnPropertyName() + { + // Arrange + var helper = DefaultTemplatesUtilities.GetHtmlHelper(); + var enumerableHelper = + DefaultTemplatesUtilities.GetHtmlHelper>(null); + + // Act + var displayNameResult = helper.DisplayName("Property1"); + var displayNameForResult = helper.DisplayNameFor(m => m.Property1); + var displayNameForEnumerableResult = enumerableHelper.DisplayNameFor(m => m.Property1); + + // Assert + Assert.Equal("Property1", displayNameResult.ToString()); + Assert.Equal("Property1", displayNameForResult.ToString()); + Assert.Equal("Property1", displayNameForEnumerableResult.ToString()); + } + + [Fact] + public void DisplayNameHelpers_ReturnPropertyName_ForNestedProperty() + { + // Arrange + var helper = DefaultTemplatesUtilities.GetHtmlHelper(model: null); + var enumerableHelper = + DefaultTemplatesUtilities.GetHtmlHelper>(model: null); + + // Act + var displayNameResult = helper.DisplayName("Inner.Id"); + var displayNameForResult = helper.DisplayNameFor(m => m.Inner.Id); + var displayNameForEnumerableResult = enumerableHelper.DisplayNameFor(m => m.Inner.Id); + + // Assert + Assert.Equal("Id", displayNameResult.ToString()); + Assert.Equal("Id", displayNameForResult.ToString()); + Assert.Equal("Id", displayNameForEnumerableResult.ToString()); + } + + [Theory] + [InlineData("")] + [InlineData("Custom property name from metadata")] + public void DisplayNameHelpers_ReturnMetadataPropertyName_IfOverridden(string propertyName) + { + // Arrange + var metadata = new ModelMetadata( + new DataAnnotationsModelMetadataProvider(), + containerType: null, + modelAccessor: null, + modelType: typeof(object), + propertyName: propertyName); + + var helper = DefaultTemplatesUtilities.GetHtmlHelper(); + helper.ViewData.ModelMetadata = metadata; + var enumerableHelper = + DefaultTemplatesUtilities.GetHtmlHelper>(null); + enumerableHelper.ViewData.ModelMetadata = metadata; + + // Act + var displayNameResult = helper.DisplayName(""); + var displayNameForResult = helper.DisplayNameFor(m => m); + var displayNameForEnumerableResult = enumerableHelper.DisplayNameFor(m => m); + var displayNameForModelResult = helper.DisplayNameForModel(); + + // Assert + Assert.Equal(propertyName, displayNameResult.ToString()); + Assert.Equal(propertyName, displayNameForResult.ToString()); + Assert.Equal(propertyName, displayNameForEnumerableResult.ToString()); + Assert.Equal(propertyName, displayNameForModelResult.ToString()); + } + + [Theory] + [InlineData("")] + [InlineData("Custom property name from metadata")] + public void DisplayNameHelpers_ReturnMetadataPropertyNameForProperty_IfOverridden(string propertyName) + { + // Arrange + var metadataHelper = new MetadataHelper(); + var metadata = new ModelMetadata( + metadataHelper.MetadataProvider.Object, + containerType: null, + modelAccessor: null, + modelType: typeof(object), + propertyName: propertyName); + metadataHelper.MetadataProvider + .Setup(provider => provider.GetMetadataForProperty(It.IsAny>(), It.IsAny(), "Property1")) + .Returns(metadata); + + var helper = DefaultTemplatesUtilities.GetHtmlHelper(metadataHelper.MetadataProvider.Object); + var enumerableHelper = + DefaultTemplatesUtilities.GetHtmlHelper>( + model: null, + provider: metadataHelper.MetadataProvider.Object); + + // Act + var displayNameForResult = helper.DisplayNameFor(m => m.Property1); + var displayNameForEnumerableResult = enumerableHelper.DisplayNameFor(m => m.Property1); + + // Assert + Assert.Equal(propertyName, displayNameForResult.ToString()); + Assert.Equal(propertyName, displayNameForEnumerableResult.ToString()); + } + + [Theory] + [InlineData("")] // Empty display name wins over non-empty property name. + [InlineData("Custom display name from metadata")] + public void DisplayNameHelpers_ReturnDisplayName_IfNonNull(string displayName) + { + // Arrange + var helper = DefaultTemplatesUtilities.GetHtmlHelper(); + helper.ViewData.ModelMetadata.DisplayName = displayName; + var enumerableHelper = + DefaultTemplatesUtilities.GetHtmlHelper>(null); + enumerableHelper.ViewData.ModelMetadata.DisplayName = displayName; + + // Act + var displayNameResult = helper.DisplayName(""); + var displayNameForResult = helper.DisplayNameFor(m => m); + var displayNameForEnumerableResult = enumerableHelper.DisplayNameFor(m => m); + var displayNameForModelResult = helper.DisplayNameForModel(); + + // Assert + Assert.Equal(displayName, displayNameResult.ToString()); + Assert.Equal(displayName, displayNameForResult.ToString()); + Assert.Equal(displayName, displayNameForEnumerableResult.ToString()); + Assert.Equal(displayName, displayNameForModelResult.ToString()); + } + + [Theory] + [InlineData("")] + [InlineData("Custom display name from metadata")] + public void DisplayNameHelpers_ReturnDisplayNameForProperty_IfNonNull(string displayName) + { + // Arrange + var metadataHelper = new MetadataHelper(); // All properties will use the same metadata. + metadataHelper.Metadata + .Setup(metadata => metadata.DisplayName) + .Returns(displayName); + var helper = DefaultTemplatesUtilities.GetHtmlHelper(metadataHelper.MetadataProvider.Object); + var enumerableHelper = + DefaultTemplatesUtilities.GetHtmlHelper>( + model: null, + provider: metadataHelper.MetadataProvider.Object); + + // Act + var displayNameResult = helper.DisplayName("Property1"); + var displayNameForResult = helper.DisplayNameFor(m => m.Property1); + var displayNameForEnumerableResult = enumerableHelper.DisplayNameFor(m => m.Property1); + + // Assert + Assert.Equal(displayName, displayNameResult.ToString()); + Assert.Equal(displayName, displayNameForResult.ToString()); + Assert.Equal(displayName, displayNameForEnumerableResult.ToString()); + } + + [Theory] + [InlineData("A", "A")] + [InlineData("A[23]", "A[23]")] + [InlineData("A[0].B", "B")] + [InlineData("A.B.C.D", "D")] + public void DisplayName_ReturnsRightmostExpressionSegment_IfPropertiesNotFound( + string expression, + string expectedResult) + { + // Arrange + var metadataHelper = new MetadataHelper(); + var helper = DefaultTemplatesUtilities.GetHtmlHelper(metadataHelper.MetadataProvider.Object); + + // Act + var result = helper.DisplayName(expression); + + // Assert + // DisplayName() falls back to expression name when DisplayName and PropertyName are null. + Assert.Equal(expectedResult, result.ToString()); + } + + [Fact] + public void DisplayNameFor_ConsultsMetadataProviderForMetadataAboutProperty() + { + // Arrange + var modelType = typeof(DefaultTemplatesUtilities.ObjectTemplateModel); + var metadataHelper = new MetadataHelper(); + metadataHelper.MetadataProvider + .Setup(p => p.GetMetadataForProperty(It.IsAny>(), modelType, "Property1")) + .Returns(metadataHelper.Metadata.Object) + .Verifiable(); + + var helper = DefaultTemplatesUtilities.GetHtmlHelper(metadataHelper.MetadataProvider.Object); + + // Act + var result = helper.DisplayNameFor(m => m.Property1); + + // Assert + metadataHelper.MetadataProvider.Verify(); + + // DisplayNameFor() falls back to expression name when DisplayName and PropertyName are null. + Assert.Equal("Property1", result.ToString()); + } + + [Fact] + public void DisplayNameFor_ThrowsInvalidOperation_IfExpressionUnsupported() + { + // Arrange + var helper = DefaultTemplatesUtilities.GetHtmlHelper(); + + // Act & Assert + var exception = Assert.Throws( + () => helper.DisplayNameFor(model => new { foo = "Bar" })); + Assert.Equal( + "Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.", + exception.Message); + } + + [Fact] + public void EnumerableDisplayNameFor_ThrowsInvalidOperation_IfExpressionUnsupported() + { + // Arrange + var helper = + DefaultTemplatesUtilities.GetHtmlHelper>(null); + + // Act & Assert + var exception = Assert.Throws( + () => helper.DisplayNameFor(model => new { foo = "Bar" })); + Assert.Equal( + "Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.", + exception.Message); + } + + [Fact] + public void DisplayNameFor_ReturnsVariableName() + { + // Arrange + var unknownKey = "this is a dummy parameter value"; + var helper = DefaultTemplatesUtilities.GetHtmlHelper(); + + // Act + var result = helper.DisplayNameFor(model => unknownKey); + + // Assert + Assert.Equal("unknownKey", result.ToString()); + } + + [Fact] + public void EnumerableDisplayNameFor_ReturnsVariableName() + { + // Arrange + var unknownKey = "this is a dummy parameter value"; + var helper = + DefaultTemplatesUtilities.GetHtmlHelper>(null); + + // Act + var result = helper.DisplayNameFor(model => unknownKey); + + // Assert + Assert.Equal("unknownKey", result.ToString()); + } + + private sealed class InnerClass + { + public int Id { get; set; } + } + + private sealed class OuterClass + { + public InnerClass Inner { get; set; } + } + + private sealed class MetadataHelper + { + public Mock Metadata { get; set; } + public Mock MetadataProvider { get; set; } + + public MetadataHelper() + { + MetadataProvider = new Mock(); + Metadata = new Mock(MetadataProvider.Object, null, null, typeof(object), null); + + MetadataProvider.Setup(p => p.GetMetadataForProperties(It.IsAny(), It.IsAny())) + .Returns(new ModelMetadata[0]); + MetadataProvider.Setup(p => p.GetMetadataForProperty(It.IsAny>(), It.IsAny(), It.IsAny())) + .Returns(Metadata.Object); + MetadataProvider.Setup(p => p.GetMetadataForType(It.IsAny>(), It.IsAny())) + .Returns(Metadata.Object); + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/HtmlHelperLabelExtensionsTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/HtmlHelperLabelExtensionsTest.cs new file mode 100644 index 0000000000..1113e3770a --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/HtmlHelperLabelExtensionsTest.cs @@ -0,0 +1,332 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using Microsoft.AspNet.Mvc.ModelBinding; +using Microsoft.AspNet.Mvc.Rendering; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Mvc.Core +{ + /// + /// Test the class. + /// + public class HtmlHelperLabelExtensionsTest + { + [Fact] + public void LabelHelpers_ReturnEmptyForModel() + { + // Arrange + var helper = DefaultTemplatesUtilities.GetHtmlHelper(); + + // Act + var labelResult = helper.Label(""); + var labelNullResult = helper.Label(expression: null); // null is another alias for current model + var labelForResult = helper.LabelFor(m => m); + var labelForModelResult = helper.LabelForModel(); + + // Assert + Assert.Empty(labelResult.ToString()); + Assert.Empty(labelNullResult.ToString()); + Assert.Empty(labelForResult.ToString()); + Assert.Empty(labelForModelResult.ToString()); + } + + [Fact] + public void LabelHelpers_DisplayPropertyName() + { + // Arrange + var helper = DefaultTemplatesUtilities.GetHtmlHelper(); + + // Act + var labelResult = helper.Label("Property1"); + var labelForResult = helper.LabelFor(m => m.Property1); + + // Assert + Assert.Equal("", labelResult.ToString()); + Assert.Equal("", labelForResult.ToString()); + } + + [Fact] + public void LabelHelpers_DisplayPropertyName_ForNestedProperty() + { + // Arrange + var helper = DefaultTemplatesUtilities.GetHtmlHelper(model: null); + + // Act + var labelResult = helper.Label("Inner.Id"); + var labelForResult = helper.LabelFor(m => m.Inner.Id); + + // Assert + Assert.Equal("", labelResult.ToString()); + Assert.Equal("", labelForResult.ToString()); + } + + [Fact] + public void LabelHelpers_ReturnEmptyForModel_IfMetadataPropertyNameEmpty() + { + // Arrange + var metadata = new ModelMetadata( + new DataAnnotationsModelMetadataProvider(), + containerType: null, + modelAccessor: null, + modelType: typeof(object), + propertyName: string.Empty); + + var helper = DefaultTemplatesUtilities.GetHtmlHelper(); + helper.ViewData.ModelMetadata = metadata; + + // Act + var labelResult = helper.Label(""); + var labelNullResult = helper.Label(expression: null); // null is another alias for current model + var labelForResult = helper.LabelFor(m => m); + var labelForModelResult = helper.LabelForModel(); + + // Assert + Assert.Empty(labelResult.ToString()); + Assert.Empty(labelNullResult.ToString()); + Assert.Empty(labelForResult.ToString()); + Assert.Empty(labelForModelResult.ToString()); + } + + [Theory] + [InlineData("MyProperty")] + [InlineData("Custom property name from metadata")] + public void LabelHelpers_DisplayMetadataPropertyName_IfOverridden(string propertyName) + { + // Arrange + var metadata = new ModelMetadata( + new DataAnnotationsModelMetadataProvider(), + containerType: null, + modelAccessor: null, + modelType: typeof(object), + propertyName: propertyName); + + var helper = DefaultTemplatesUtilities.GetHtmlHelper(); + helper.ViewData.ModelMetadata = metadata; + + // Act + var labelResult = helper.Label(""); + var labelForResult = helper.LabelFor(m => m); + var labelForModelResult = helper.LabelForModel(); + + // Assert + Assert.Equal("", labelResult.ToString()); + Assert.Equal("", labelForResult.ToString()); + Assert.Equal("", labelForModelResult.ToString()); + } + + [Theory] + [InlineData("MyProperty")] + [InlineData("Custom property name from metadata")] + public void LabelHelpers_DisplayMetadataPropertyNameForProperty_IfOverridden(string propertyName) + { + // Arrange + var metadataHelper = new MetadataHelper(); + var metadata = new ModelMetadata( + metadataHelper.MetadataProvider.Object, + containerType: null, + modelAccessor: null, + modelType: typeof(object), + propertyName: propertyName); + metadataHelper.MetadataProvider + .Setup(provider => provider.GetMetadataForProperty(It.IsAny>(), It.IsAny(), "Property1")) + .Returns(metadata); + + var helper = DefaultTemplatesUtilities.GetHtmlHelper(metadataHelper.MetadataProvider.Object); + + // Act + var labelForResult = helper.LabelFor(m => m.Property1); + + // Assert + Assert.Equal("", labelForResult.ToString()); + } + + [Fact] + public void LabelHelpers_ReturnEmptyForModel_IfDisplayNameEmpty() + { + // Arrange + var helper = DefaultTemplatesUtilities.GetHtmlHelper(); + helper.ViewData.ModelMetadata.DisplayName = string.Empty; + + // Act + var labelResult = helper.Label(""); + var labelNullResult = helper.Label(expression: null); // null is another alias for current model + var labelForResult = helper.LabelFor(m => m); + var labelForModelResult = helper.LabelForModel(); + + // Assert + Assert.Empty(labelResult.ToString()); + Assert.Empty(labelNullResult.ToString()); + Assert.Empty(labelForResult.ToString()); + Assert.Empty(labelForModelResult.ToString()); + } + + [Theory] + [InlineData("DisplayName")] + [InlineData("Custom display name from metadata")] + public void LabelHelpers_DisplayDisplayName_IfNonNull(string displayName) + { + // Arrange + var helper = DefaultTemplatesUtilities.GetHtmlHelper(); + helper.ViewData.ModelMetadata.DisplayName = displayName; + + // Act + var labelResult = helper.Label(""); + var labelForResult = helper.LabelFor(m => m); + var labelForModelResult = helper.LabelForModel(); + + // Assert + Assert.Equal("", labelResult.ToString()); + Assert.Equal("", labelForResult.ToString()); + Assert.Equal("", labelForModelResult.ToString()); + } + + [Fact] + public void LabelHelpers_ReturnEmptyForProperty_IfDisplayNameEmpty() + { + // Arrange + var metadataHelper = new MetadataHelper(); // All properties will use the same metadata. + metadataHelper.Metadata + .Setup(metadata => metadata.DisplayName) + .Returns(string.Empty); + var helper = DefaultTemplatesUtilities.GetHtmlHelper(metadataHelper.MetadataProvider.Object); + + // Act + var labelResult = helper.Label(""); + var labelNullResult = helper.Label(expression: null); // null is another alias for current model + var labelForResult = helper.LabelFor(m => m); + var labelForModelResult = helper.LabelForModel(); + + // Assert + Assert.Empty(labelResult.ToString()); + Assert.Empty(labelNullResult.ToString()); + Assert.Empty(labelForResult.ToString()); + Assert.Empty(labelForModelResult.ToString()); + } + + [Theory] + [InlineData("DisplayName")] + [InlineData("Custom display name from metadata")] + public void LabelHelpers_DisplayDisplayNameForProperty_IfNonNull(string displayName) + { + // Arrange + var metadataHelper = new MetadataHelper(); // All properties will use the same metadata. + metadataHelper.Metadata + .Setup(metadata => metadata.DisplayName) + .Returns(displayName); + var helper = DefaultTemplatesUtilities.GetHtmlHelper(metadataHelper.MetadataProvider.Object); + + // Act + var labelResult = helper.Label("Property1"); + var labelForResult = helper.LabelFor(m => m.Property1); + + // Assert + Assert.Equal("", labelResult.ToString()); + Assert.Equal("", labelForResult.ToString()); + } + + [Theory] + [InlineData("A", "A")] + [InlineData("A[23]", "A[23]")] + [InlineData("A[0].B", "B")] + [InlineData("A.B.C.D", "D")] + public void Label_DisplaysRightmostExpressionSegment_IfPropertiesNotFound( + string expression, + string expectedResult) + { + // Arrange + var metadataHelper = new MetadataHelper(); + var helper = DefaultTemplatesUtilities.GetHtmlHelper(metadataHelper.MetadataProvider.Object); + + // Act + var result = helper.Label(expression); + + // Assert + // Label() falls back to expression name when DisplayName and PropertyName are null. + Assert.Equal("", result.ToString()); + } + + [Fact] + public void LabelFor_ConsultsMetadataProviderForMetadataAboutProperty() + { + // Arrange + var modelType = typeof(DefaultTemplatesUtilities.ObjectTemplateModel); + var metadataHelper = new MetadataHelper(); + metadataHelper.MetadataProvider + .Setup(p => p.GetMetadataForProperty(It.IsAny>(), modelType, "Property1")) + .Returns(metadataHelper.Metadata.Object) + .Verifiable(); + + var helper = DefaultTemplatesUtilities.GetHtmlHelper(metadataHelper.MetadataProvider.Object); + + // Act + var result = helper.LabelFor(m => m.Property1); + + // Assert + metadataHelper.MetadataProvider.Verify(); + + // LabelFor() falls back to expression name when DisplayName and PropertyName are null. + Assert.Equal("", result.ToString()); + } + + [Fact] + public void LabelFor_ThrowsInvalidOperation_IfExpressionUnsupported() + { + // Arrange + var helper = DefaultTemplatesUtilities.GetHtmlHelper(); + + // Act & Assert + var exception = Assert.Throws( + () => helper.LabelFor(model => new { foo = "Bar" })); + Assert.Equal( + "Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.", + exception.Message); + } + + [Fact] + public void LabelFor_DisplaysVariableName() + { + // Arrange + var unknownKey = "this is a dummy parameter value"; + var helper = DefaultTemplatesUtilities.GetHtmlHelper(); + + // Act + var result = helper.LabelFor(model => unknownKey); + + // Assert + Assert.Equal("", result.ToString()); + } + + private sealed class InnerClass + { + public int Id { get; set; } + } + + private sealed class OuterClass + { + public InnerClass Inner { get; set; } + } + + private sealed class MetadataHelper + { + public Mock Metadata { get; set; } + public Mock MetadataProvider { get; set; } + + public MetadataHelper() + { + MetadataProvider = new Mock(); + Metadata = new Mock(MetadataProvider.Object, null, null, typeof(object), null); + + MetadataProvider.Setup(p => p.GetMetadataForProperties(It.IsAny(), It.IsAny())) + .Returns(new ModelMetadata[0]); + MetadataProvider.Setup(p => p.GetMetadataForProperty(It.IsAny>(), It.IsAny(), It.IsAny())) + .Returns(Metadata.Object); + MetadataProvider.Setup(p => p.GetMetadataForType(It.IsAny>(), It.IsAny())) + .Returns(Metadata.Object); + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/HtmlHelperNameExtensionsTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/HtmlHelperNameExtensionsTest.cs new file mode 100644 index 0000000000..40fdb737cd --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/HtmlHelperNameExtensionsTest.cs @@ -0,0 +1,255 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Mvc.ModelBinding; +using Microsoft.AspNet.Mvc.Rendering; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Mvc.Core +{ + /// + /// Test the class. + /// + /// + /// TODO #704: When that bug is fixed and Id() behaves differently than Name(), will need to break some + /// test methods below in two. + /// + public class HtmlHelperNameExtensionsTest + { + [Fact] + public void IdAndNameHelpers_ReturnEmptyForModel() + { + // Arrange + var helper = DefaultTemplatesUtilities.GetHtmlHelper(); + + // Act + var idResult = helper.Id(""); + var idNullResult = helper.Id(name: null); // null is another alias for current model + var idForResult = helper.IdFor(m => m); + var idForModelResult = helper.IdForModel(); + var nameResult = helper.Name(""); + var nameNullResult = helper.Name(name: null); + var nameForResult = helper.NameFor(m => m); + var nameForModelResult = helper.NameForModel(); + + // Assert + Assert.Empty(idResult.ToString()); + Assert.Empty(idNullResult.ToString()); + Assert.Empty(idForResult.ToString()); + Assert.Empty(idForModelResult.ToString()); + Assert.Empty(nameResult.ToString()); + Assert.Empty(nameNullResult.ToString()); + Assert.Empty(nameForResult.ToString()); + Assert.Empty(nameForModelResult.ToString()); + } + + [Theory] + [InlineData("")] + [InlineData("A")] + [InlineData("A[23]")] + [InlineData("A[0].B")] + [InlineData("A.B.C.D")] + public void IdAndNameHelpers_ReturnPrefixForModel(string prefix) + { + // Arrange + var helper = DefaultTemplatesUtilities.GetHtmlHelper(); + helper.ViewData.TemplateInfo.HtmlFieldPrefix = prefix; + + // Act + var idResult = helper.Id(""); + var idForResult = helper.IdFor(m => m); + var idForModelResult = helper.IdForModel(); + var nameResult = helper.Name(""); + var nameForResult = helper.NameFor(m => m); + var nameForModelResult = helper.NameForModel(); + + // Assert + Assert.Equal(prefix, idResult.ToString()); + Assert.Equal(prefix, idForResult.ToString()); + Assert.Equal(prefix, idForModelResult.ToString()); + Assert.Equal(prefix, nameResult.ToString()); + Assert.Equal(prefix, nameForResult.ToString()); + Assert.Equal(prefix, nameForModelResult.ToString()); + } + + [Fact] + public void IdAndNameHelpers_ReturnPropertyName() + { + // Arrange + var helper = DefaultTemplatesUtilities.GetHtmlHelper(); + + // Act + var idResult = helper.Id("Property1"); + var idForResult = helper.IdFor(m => m.Property1); + var nameResult = helper.Name("Property1"); + var nameForResult = helper.NameFor(m => m.Property1); + + // Assert + Assert.Equal("Property1", idResult.ToString()); + Assert.Equal("Property1", idForResult.ToString()); + Assert.Equal("Property1", nameResult.ToString()); + Assert.Equal("Property1", nameForResult.ToString()); + } + + [Theory] + [InlineData(null, "Property1")] + [InlineData("", "Property1")] + [InlineData("A", "A.Property1")] + [InlineData("A[23]", "A[23].Property1")] + [InlineData("A[0].B", "A[0].B.Property1")] + [InlineData("A.B.C.D", "A.B.C.D.Property1")] + public void IdAndNameHelpers_ReturnPrefixAndPropertyName(string prefix, string expectedResult) + { + // Arrange + var helper = DefaultTemplatesUtilities.GetHtmlHelper(); + + // Act + var idResult = helper.Id("Property1"); + var idForResult = helper.IdFor(m => m.Property1); + var nameResult = helper.Name("Property1"); + var nameForResult = helper.NameFor(m => m.Property1); + + // Assert + Assert.Equal("Property1", idResult.ToString()); + Assert.Equal("Property1", idForResult.ToString()); + Assert.Equal("Property1", nameResult.ToString()); + Assert.Equal("Property1", nameForResult.ToString()); + } + + [Fact] + public void IdAndNameHelpers_ReturnPropertyPath_ForNestedProperty() + { + // Arrange + var helper = DefaultTemplatesUtilities.GetHtmlHelper(model: null); + + // Act + var idResult = helper.Id("Inner.Id"); + var idForResult = helper.IdFor(m => m.Inner.Id); + var nameResult = helper.Name("Inner.Id"); + var nameForResult = helper.NameFor(m => m.Inner.Id); + + // Assert + Assert.Equal("Inner.Id", idResult.ToString()); + Assert.Equal("Inner.Id", idForResult.ToString()); + Assert.Equal("Inner.Id", nameResult.ToString()); + Assert.Equal("Inner.Id", nameForResult.ToString()); + } + + [Fact] + public void IdAndNameHelpers_DoNotConsultMetadataOrMetadataProvider() + { + // Arrange + var provider = new Mock(MockBehavior.Strict); + var metadata = + new Mock(MockBehavior.Strict, provider.Object, null, null, typeof(object), null); + provider + .Setup(m => m.GetMetadataForType( + It.IsAny>(), + typeof(DefaultTemplatesUtilities.ObjectTemplateModel))) + .Returns(metadata.Object); + var helper = DefaultTemplatesUtilities.GetHtmlHelper(provider.Object); + + // Act (do not throw) + var idResult = helper.Id(""); + var idForResult = helper.IdFor(m => m); + var idForModelResult = helper.IdForModel(); + var nameResult = helper.Name(""); + var nameForResult = helper.NameFor(m => m); + var nameForModelResult = helper.NameForModel(); + + // Assert + // Only the ViewDataDictionary should do anything with metadata. + provider.Verify( + m => m.GetMetadataForType(It.IsAny>(), typeof(DefaultTemplatesUtilities.ObjectTemplateModel)), + Times.Once); + } + + [Fact] + public void IdAndNameHelpers_DoNotConsultMetadataOrMetadataProvider_ForProperty() + { + // Arrange + var provider = new Mock(MockBehavior.Strict); + var metadata = + new Mock(MockBehavior.Strict, provider.Object, null, null, typeof(object), null); + provider + .Setup(m => m.GetMetadataForType( + It.IsAny>(), + typeof(DefaultTemplatesUtilities.ObjectTemplateModel))) + .Returns(metadata.Object); + var helper = DefaultTemplatesUtilities.GetHtmlHelper(provider.Object); + + // Act (do not throw) + var idResult = helper.Id("Property1"); + var idForResult = helper.IdFor(m => m.Property1); + var nameResult = helper.Name("Property1"); + var nameForResult = helper.NameFor(m => m.Property1); + + // Assert + // Only the ViewDataDictionary should do anything with metadata. + provider.Verify( + m => m.GetMetadataForType(It.IsAny>(), typeof(DefaultTemplatesUtilities.ObjectTemplateModel)), + Times.Once); + } + + [Theory] + [InlineData("A")] + [InlineData("A[0].B")] + [InlineData("A.B.C.D")] + public void IdAndName_ReturnExpression_EvenIfExpressionNotFound(string expression) + { + // Arrange + var helper = DefaultTemplatesUtilities.GetHtmlHelper(); + + // Act + var idResult = helper.Id(expression); + var nameResult = helper.Name(expression); + + // Assert + Assert.Equal(expression, idResult.ToString()); + Assert.Equal(expression, nameResult.ToString()); + } + + [Fact] + public void IdForAndNameFor_ReturnEmpty_IfExpressionUnsupported() + { + // Arrange + var helper = DefaultTemplatesUtilities.GetHtmlHelper(); + + // Act + var idResult = helper.IdFor(model => new { foo = "Bar" }); + var nameResult = helper.NameFor(model => new { foo = "Bar" }); + + // Assert + Assert.Empty(idResult.ToString()); + Assert.Empty(nameResult.ToString()); + } + + [Fact] + public void IdForAndNameFor_ReturnVariableName() + { + // Arrange + var unknownKey = "this is a dummy parameter value"; + var helper = DefaultTemplatesUtilities.GetHtmlHelper(); + + // Act + var idResult = helper.IdFor(model => unknownKey); + var nameResult = helper.NameFor(model => unknownKey); + + // Assert + Assert.Equal("unknownKey", idResult.ToString()); + Assert.Equal("unknownKey", nameResult.ToString()); + } + + private sealed class InnerClass + { + public int Id { get; set; } + } + + private sealed class OuterClass + { + public InnerClass Inner { get; set; } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/CachedDataAnnotationsMetadataAttributesTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/CachedDataAnnotationsMetadataAttributesTest.cs new file mode 100644 index 0000000000..571d99ef0e --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/CachedDataAnnotationsMetadataAttributesTest.cs @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using Xunit; + +namespace Microsoft.AspNet.Mvc.ModelBinding +{ + /// + /// Test the class. + /// + public class CachedDataAnnotationsMetadataAttributesTest + { + [Fact] + public void Constructor_DefaultsAllPropertiesToNull() + { + // Arrange + var attributes = Enumerable.Empty(); + + // Act + var cache = new CachedDataAnnotationsMetadataAttributes(attributes); + + // Assert + Assert.Null(cache.Display); + Assert.Null(cache.DisplayColumn); + Assert.Null(cache.DisplayFormat); + Assert.Null(cache.Editable); + Assert.Null(cache.Required); + } + + public static TheoryData> + ExpectedAttributeData + { + get + { + 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 + }, + }; + } + } + + [Theory] + [MemberData("ExpectedAttributeData")] + public void Constructor_FindsExpectedAttribute( + Attribute attribute, + Func accessor) + { + // Arrange + var attributes = new[] { attribute }; + + // Act + var cache = new CachedDataAnnotationsMetadataAttributes(attributes); + var result = accessor(cache); + + // Assert + Assert.Same(attribute, result); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/CachedDataAnnotationsModelMetadataTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/CachedDataAnnotationsModelMetadataTest.cs new file mode 100644 index 0000000000..3af0eefeec --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/CachedDataAnnotationsModelMetadataTest.cs @@ -0,0 +1,155 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using Xunit; + +namespace Microsoft.AspNet.Mvc.ModelBinding +{ + /// + /// Test the class. + /// + public class CachedDataAnnotationsModelMetadataTest + { + [Fact] + public void Constructor_DefersDefaultsToBaseModelMetadata() + { + // Arrange + var attributes = Enumerable.Empty(); + var provider = new DataAnnotationsModelMetadataProvider(); + + // Act + var metadata = new CachedDataAnnotationsModelMetadata( + provider, + containerType: null, + modelType: typeof(object), + propertyName: null, + attributes: attributes); + + // Assert + Assert.True(metadata.ConvertEmptyStringToNull); + Assert.False(metadata.IsReadOnly); + Assert.False(metadata.IsRequired); + + Assert.Null(metadata.Description); + Assert.Null(metadata.DisplayName); + Assert.Null(metadata.NullDisplayText); + } + + public static TheoryData> ExpectedAttributeDataStrings + { + get + { + return new TheoryData> + { + { + new DisplayAttribute { Description = "value" }, + (ModelMetadata metadata) => metadata.Description + }, + { + new DisplayAttribute { Name = "value" }, + (ModelMetadata metadata) => metadata.DisplayName + }, + { + new DisplayColumnAttribute("Property"), + (ModelMetadata metadata) => metadata.SimpleDisplayText + }, + { + new DisplayFormatAttribute { NullDisplayText = "value" }, + (ModelMetadata metadata) => metadata.NullDisplayText + }, + }; + } + } + + [Theory] + [MemberData("ExpectedAttributeDataStrings")] + public void AttributesOverrideMetadataStrings(Attribute attribute, Func accessor) + { + // Arrange + var attributes = new[] { attribute }; + var provider = new DataAnnotationsModelMetadataProvider(); + + // Act + var metadata = new CachedDataAnnotationsModelMetadata( + provider, + containerType: null, + modelType: typeof(ClassWithDisplayableColumn), + propertyName: null, + attributes: attributes) + { + Model = new ClassWithDisplayableColumn { Property = "value" }, + }; + var result = accessor(metadata); + + // Assert + Assert.Equal("value", result); + } + + public static TheoryData, bool> ExpectedAttributeDataBooleans + { + get + { + return new TheoryData, bool> + { + { + new DisplayFormatAttribute { ConvertEmptyStringToNull = false }, + (ModelMetadata metadata) => metadata.ConvertEmptyStringToNull, + false + }, + { + new DisplayFormatAttribute { ConvertEmptyStringToNull = true }, + (ModelMetadata metadata) => metadata.ConvertEmptyStringToNull, + true + }, + { + new EditableAttribute(allowEdit: false), + (ModelMetadata metadata) => metadata.IsReadOnly, + true + }, + { + new EditableAttribute(allowEdit: true), + (ModelMetadata metadata) => metadata.IsReadOnly, + false + }, + { + new RequiredAttribute(), + (ModelMetadata metadata) => metadata.IsRequired, + true + }, + }; + } + } + + [Theory] + [MemberData("ExpectedAttributeDataBooleans")] + public void AttributesOverrideMetadataBooleans( + Attribute attribute, + Func accessor, + bool expectedResult) + { + // Arrange + var attributes = new[] { attribute }; + var provider = new DataAnnotationsModelMetadataProvider(); + + // Act + var metadata = new CachedDataAnnotationsModelMetadata( + provider, + containerType: null, + modelType: typeof(object), + propertyName: null, + attributes: attributes); + var result = accessor(metadata); + + // Assert + Assert.Equal(expectedResult, result); + } + + private class ClassWithDisplayableColumn + { + public string Property { 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 5d51a73881..3daac32738 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/ModelMetadataTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/ModelMetadataTest.cs @@ -28,12 +28,21 @@ namespace Microsoft.AspNet.Mvc.ModelBinding // Assert Assert.Equal(typeof(Exception), metadata.ContainerType); Assert.True(metadata.ConvertEmptyStringToNull); - Assert.Null(metadata.NullDisplayText); + Assert.False(metadata.IsComplexType); + Assert.False(metadata.IsReadOnly); + Assert.False(metadata.IsRequired); + Assert.Null(metadata.Description); + Assert.Null(metadata.DisplayFormatString); + Assert.Null(metadata.DisplayName); + Assert.Null(metadata.EditFormatString); + Assert.Null(metadata.NullDisplayText); + Assert.Null(metadata.TemplateHint); + Assert.Equal("model", metadata.Model); + Assert.Equal("model", metadata.SimpleDisplayText); Assert.Equal(typeof(string), metadata.ModelType); Assert.Equal("propertyName", metadata.PropertyName); - Assert.False(metadata.IsReadOnly); } #endif @@ -195,6 +204,23 @@ namespace Microsoft.AspNet.Mvc.ModelBinding // GetDisplayName() + [Fact] + public void GetDisplayName_ReturnsDisplayName_IfSet() + { + // Arrange + var provider = new EmptyModelMetadataProvider(); + var metadata = new ModelMetadata(provider, null, () => null, typeof(object), "unusedName") + { + DisplayName = "displayName", + }; + + // Act + var result = metadata.GetDisplayName(); + + // Assert + Assert.Equal("displayName", result); + } + #if NET45 [Fact] public void ReturnsPropertyNameWhenSetAndDisplayNameIsNull() @@ -224,6 +250,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding Assert.Equal("Object", result); } #endif + // SimpleDisplayText public static IEnumerable SimpleDisplayTextData diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Microsoft.AspNet.Mvc.ModelBinding.Test.kproj b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Microsoft.AspNet.Mvc.ModelBinding.Test.kproj index 2851854fe0..59df67f936 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Microsoft.AspNet.Mvc.ModelBinding.Test.kproj +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Microsoft.AspNet.Mvc.ModelBinding.Test.kproj @@ -37,7 +37,9 @@ + +