diff --git a/samples/TagHelperSample.Web/Views/Home/Create.cshtml b/samples/TagHelperSample.Web/Views/Home/Create.cshtml index 96054bb801..e7bee3837f 100644 --- a/samples/TagHelperSample.Web/Views/Home/Create.cshtml +++ b/samples/TagHelperSample.Web/Views/Home/Create.cshtml @@ -14,13 +14,14 @@
@* validation summary tag helper will target just
elements and append the list of errors *@ @* - i.e. this helper, like
diff --git a/src/Microsoft.AspNet.Mvc.TagHelpers/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Mvc.TagHelpers/Properties/Resources.Designer.cs index f44a44fdb6..183d549f26 100644 --- a/src/Microsoft.AspNet.Mvc.TagHelpers/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNet.Mvc.TagHelpers/Properties/Resources.Designer.cs @@ -154,6 +154,22 @@ namespace Microsoft.AspNet.Mvc.TagHelpers return string.Format(CultureInfo.CurrentCulture, GetString("TagHelpers_NoProvidedMetadata"), p0, p1, p2, p3); } + /// + /// The value of argument '{0}' ({1}) is invalid for Enum type '{2}'. + /// + internal static string InvalidEnumArgument + { + get { return GetString("InvalidEnumArgument"); } + } + + /// + /// The value of argument '{0}' ({1}) is invalid for Enum type '{2}'. + /// + internal static string FormatInvalidEnumArgument(object p0, object p1, object p2) + { + return string.Format(CultureInfo.CurrentCulture, GetString("InvalidEnumArgument"), p0, p1, p2); + } + private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); diff --git a/src/Microsoft.AspNet.Mvc.TagHelpers/Resources.resx b/src/Microsoft.AspNet.Mvc.TagHelpers/Resources.resx index 75ccdd5b0b..43bda290f1 100644 --- a/src/Microsoft.AspNet.Mvc.TagHelpers/Resources.resx +++ b/src/Microsoft.AspNet.Mvc.TagHelpers/Resources.resx @@ -144,4 +144,7 @@ The {2} was unable to provide metadata about '{1}' expression value '{3}' for {0}. + + The value of argument '{0}' ({1}) is invalid for Enum type '{2}'. + \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.TagHelpers/ValidationSummary.cs b/src/Microsoft.AspNet.Mvc.TagHelpers/ValidationSummary.cs index 5b7c111c06..e56dcdc582 100644 --- a/src/Microsoft.AspNet.Mvc.TagHelpers/ValidationSummary.cs +++ b/src/Microsoft.AspNet.Mvc.TagHelpers/ValidationSummary.cs @@ -1,7 +1,7 @@ // 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. -namespace Microsoft.AspNet.Mvc.TagHelpers +namespace Microsoft.AspNet.Mvc { /// /// Acceptable validation summary rendering modes. diff --git a/src/Microsoft.AspNet.Mvc.TagHelpers/ValidationSummaryTagHelper.cs b/src/Microsoft.AspNet.Mvc.TagHelpers/ValidationSummaryTagHelper.cs index 3463b9acec..480945d9ff 100644 --- a/src/Microsoft.AspNet.Mvc.TagHelpers/ValidationSummaryTagHelper.cs +++ b/src/Microsoft.AspNet.Mvc.TagHelpers/ValidationSummaryTagHelper.cs @@ -17,6 +17,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers public class ValidationSummaryTagHelper : TagHelper { private const string ValidationSummaryAttributeName = "asp-validation-summary"; + private ValidationSummary _validationSummary; // Protected to ensure subclasses are correctly activated. Internal for ease of use when testing. [Activate] @@ -27,51 +28,60 @@ namespace Microsoft.AspNet.Mvc.TagHelpers protected internal IHtmlGenerator Generator { get; set; } /// - /// If All or ModelOnly, appends a validation summary. Acceptable values are defined by the - /// enum. + /// If or , appends a validation + /// summary. Otherwise (, the default), this tag helper does nothing. /// + /// + /// Thrown if setter is called with an undefined value e.g. + /// (ValidationSummary)23. + /// [HtmlAttributeName(ValidationSummaryAttributeName)] - public string ValidationSummaryValue { get; set; } + public ValidationSummary ValidationSummary + { + get + { + return _validationSummary; + } + set + { + switch (value) + { + case ValidationSummary.All: + case ValidationSummary.ModelOnly: + case ValidationSummary.None: + _validationSummary = value; + break; + + default: + throw new ArgumentException( + message: Resources.FormatInvalidEnumArgument( + nameof(value), + value, + typeof(ValidationSummary).FullName), + paramName: nameof(value)); + } + } + } /// - /// Does nothing if is null, empty or "None". - /// - /// Thrown if is not a valid value. - /// + /// Does nothing if is . public override void Process(TagHelperContext context, TagHelperOutput output) { - if (!string.IsNullOrEmpty(ValidationSummaryValue)) + if (ValidationSummary == ValidationSummary.None) { - ValidationSummary validationSummaryValue; - if (!Enum.TryParse(ValidationSummaryValue, ignoreCase: true, result: out validationSummaryValue)) - { - throw new InvalidOperationException( - Resources.FormatTagHelpers_InvalidValue_ThreeAcceptableValues( - "
", - ValidationSummaryAttributeName, - ValidationSummaryValue, - ValidationSummary.All, - ValidationSummary.ModelOnly, - ValidationSummary.None)); - } - else if (validationSummaryValue == ValidationSummary.None) - { - return; - } + return; + } - var validationModelErrorsOnly = validationSummaryValue == ValidationSummary.ModelOnly; - var tagBuilder = Generator.GenerateValidationSummary( - ViewContext, - excludePropertyErrors: validationModelErrorsOnly, - message: null, - headerTag: null, - htmlAttributes: null); - - if (tagBuilder != null) - { - output.MergeAttributes(tagBuilder); - output.Content += tagBuilder.InnerHtml; - } + var tagBuilder = Generator.GenerateValidationSummary( + ViewContext, + excludePropertyErrors: ValidationSummary == ValidationSummary.ModelOnly, + message: null, + headerTag: null, + htmlAttributes: null); + if (tagBuilder != null) + { + output.MergeAttributes(tagBuilder); + output.Content += tagBuilder.InnerHtml; } } } diff --git a/test/Microsoft.AspNet.Mvc.TagHelpers.Test/ValidationSummaryTagHelperTest.cs b/test/Microsoft.AspNet.Mvc.TagHelpers.Test/ValidationSummaryTagHelperTest.cs index bde704137a..bdad85e1a3 100644 --- a/test/Microsoft.AspNet.Mvc.TagHelpers.Test/ValidationSummaryTagHelperTest.cs +++ b/test/Microsoft.AspNet.Mvc.TagHelpers.Test/ValidationSummaryTagHelperTest.cs @@ -10,6 +10,7 @@ using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Razor.Runtime.TagHelpers; using Microsoft.AspNet.Routing; +using Microsoft.AspNet.Testing; using Moq; using Xunit; @@ -17,6 +18,17 @@ namespace Microsoft.AspNet.Mvc.TagHelpers { public class ValidationSummaryTagHelperTest { + public void Concstructor_IntializesProperties_AsExpected() + { + // Arrange & Act + var validationSummaryTagHelper = new ValidationSummaryTagHelper(); + + // Assert + Assert.Null(validationSummaryTagHelper.Generator); + Assert.Null(validationSummaryTagHelper.ViewContext); + Assert.Equal(ValidationSummary.None, validationSummaryTagHelper.ValidationSummary); + } + [Fact] public async Task ProcessAsync_GeneratesExpectedOutput() { @@ -25,7 +37,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers var metadataProvider = new DataAnnotationsModelMetadataProvider(); var validationSummaryTagHelper = new ValidationSummaryTagHelper { - ValidationSummaryValue = "All" + ValidationSummary = ValidationSummary.All, }; var tagHelperContext = new TagHelperContext(new Dictionary(), uniqueId: "test"); @@ -57,13 +69,17 @@ namespace Microsoft.AspNet.Mvc.TagHelpers Assert.Equal(expectedTagName, output.TagName); } - [Fact] - public async Task ProcessAsync_CallsIntoGenerateValidationSummaryWithExpectedParameters() + [Theory] + [InlineData(ValidationSummary.All, false)] + [InlineData(ValidationSummary.ModelOnly, true)] + public async Task ProcessAsync_CallsIntoGenerateValidationSummaryWithExpectedParameters( + ValidationSummary validationSummary, + bool expectedExcludePropertyErrors) { // Arrange var validationSummaryTagHelper = new ValidationSummaryTagHelper { - ValidationSummaryValue = "ModelOnly", + ValidationSummary = validationSummary, }; var output = new TagHelperOutput( "div", @@ -72,7 +88,12 @@ namespace Microsoft.AspNet.Mvc.TagHelpers var expectedViewContext = CreateViewContext(); var generator = new Mock(); generator - .Setup(mock => mock.GenerateValidationSummary(expectedViewContext, true, null, null, null)) + .Setup(mock => mock.GenerateValidationSummary( + expectedViewContext, + expectedExcludePropertyErrors, + null, // message + null, // headerTag + null)) // htmlAttributes .Returns(new TagBuilder("div")) .Verifiable(); validationSummaryTagHelper.ViewContext = expectedViewContext; @@ -93,7 +114,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers // Arrange var validationSummaryTagHelper = new ValidationSummaryTagHelper { - ValidationSummaryValue = "ModelOnly" + ValidationSummary = ValidationSummary.ModelOnly, }; var output = new TagHelperOutput( "div", @@ -136,15 +157,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers Assert.Equal("Content of validation summaryNew HTML", output.Content); } - [Theory] - [InlineData("")] - [InlineData(null)] - public async Task ProcessAsync_DoesNothingIfNullOrEmptyValidationSummaryValue(string validationSummaryValue) + [Fact] + public async Task ProcessAsync_DoesNothingIfValidationSummaryNone() { // Arrange var validationSummaryTagHelper = new ValidationSummaryTagHelper { - ValidationSummaryValue = validationSummaryValue + ValidationSummary = ValidationSummary.None, }; var output = new TagHelperOutput( "div", @@ -166,16 +185,14 @@ namespace Microsoft.AspNet.Mvc.TagHelpers } [Theory] - [InlineData("All")] - [InlineData("all")] - [InlineData("ModelOnly")] - [InlineData("modelonly")] - public async Task ProcessAsync_GeneratesValidationSummaryWhenNotNone_IgnoresCase(string validationSummary) + [InlineData(ValidationSummary.All)] + [InlineData(ValidationSummary.ModelOnly)] + public async Task ProcessAsync_GeneratesValidationSummaryWhenNotNone(ValidationSummary validationSummary) { // Arrange var validationSummaryTagHelper = new ValidationSummaryTagHelper { - ValidationSummaryValue = validationSummary + ValidationSummary = validationSummary, }; var output = new TagHelperOutput( "div", @@ -211,56 +228,24 @@ namespace Microsoft.AspNet.Mvc.TagHelpers } [Theory] - [InlineData("None")] - [InlineData("none")] - public async Task ProcessAsync_DoesNotGenerateValidationSummaryWhenNone_IgnoresCase(string validationSummary) + [InlineData((ValidationSummary)(-1))] + [InlineData((ValidationSummary)23)] + [InlineData(ValidationSummary.All | ValidationSummary.ModelOnly)] + [ReplaceCulture] + public void ValidationSummaryProperty_ThrowsWhenSetToInvalidValidationSummaryValue( + ValidationSummary validationSummary) { // Arrange - var validationSummaryTagHelper = new ValidationSummaryTagHelper - { - ValidationSummaryValue = validationSummary - }; - var output = new TagHelperOutput( - "div", - attributes: new Dictionary(), - content: "Content of validation message"); - var tagBuilder = new TagBuilder("span2") - { - InnerHtml = "New HTML" - }; + var validationSummaryTagHelper = new ValidationSummaryTagHelper(); + var expectedMessage = string.Format( + @"The value of argument 'value' ({0}) is invalid for Enum type 'Microsoft.AspNet.Mvc.ValidationSummary'. +Parameter name: value", + validationSummary); - var generator = new Mock(MockBehavior.Strict); - - // Act - await validationSummaryTagHelper.ProcessAsync(context: null, output: output); - - // Assert - Assert.Equal("div", output.TagName); - Assert.Empty(output.Attributes); - Assert.Equal("Content of validation message", output.Content); - } - - [Fact] - public async Task ProcessAsync_ThrowsWhenInvalidValidationSummaryValue() - { - // Arrange - var validationSummaryTagHelper = new ValidationSummaryTagHelper - { - ValidationSummaryValue = "Hello World" - }; - var output = new TagHelperOutput( - "div", - attributes: new Dictionary(), - content: "Content of validation message"); - var expectedViewContext = CreateViewContext(); - var expectedMessage = "Cannot parse 'asp-validation-summary' value 'Hello World' for
. Acceptable " + - "values are 'All', 'ModelOnly' and 'None'."; - - // Act - var ex = await Assert.ThrowsAsync( - () => validationSummaryTagHelper.ProcessAsync(context: null, output: output)); - - // Assert + // Act & Assert + var ex = Assert.Throws( + "value", + () => { validationSummaryTagHelper.ValidationSummary = validationSummary; }); Assert.Equal(expectedMessage, ex.Message); } diff --git a/test/WebSites/MvcTagHelpersWebSite/Views/MvcTagHelper_Home/CreateWarehouse.cshtml b/test/WebSites/MvcTagHelpersWebSite/Views/MvcTagHelper_Home/CreateWarehouse.cshtml index 8e2063b18d..8502487954 100644 --- a/test/WebSites/MvcTagHelpersWebSite/Views/MvcTagHelper_Home/CreateWarehouse.cshtml +++ b/test/WebSites/MvcTagHelpersWebSite/Views/MvcTagHelper_Home/CreateWarehouse.cshtml @@ -20,7 +20,7 @@ } diff --git a/test/WebSites/MvcTagHelpersWebSite/Views/MvcTagHelper_Home/Order.cshtml b/test/WebSites/MvcTagHelpersWebSite/Views/MvcTagHelper_Home/Order.cshtml index 703213b130..a380d5d4e4 100644 --- a/test/WebSites/MvcTagHelpersWebSite/Views/MvcTagHelper_Home/Order.cshtml +++ b/test/WebSites/MvcTagHelpersWebSite/Views/MvcTagHelper_Home/Order.cshtml @@ -75,7 +75,7 @@ @Html.EditorFor(model => model.Customer.Gender)
-
+
diff --git a/test/WebSites/MvcTagHelpersWebSite/Views/Shared/Customer.cshtml b/test/WebSites/MvcTagHelpersWebSite/Views/Shared/Customer.cshtml index c26d5646ce..8209b65bda 100644 --- a/test/WebSites/MvcTagHelpersWebSite/Views/Shared/Customer.cshtml +++ b/test/WebSites/MvcTagHelpersWebSite/Views/Shared/Customer.cshtml @@ -38,8 +38,8 @@ Female
-
-
+
+
diff --git a/test/WebSites/TagHelpersWebSite/Views/Employee/Create.cshtml b/test/WebSites/TagHelpersWebSite/Views/Employee/Create.cshtml index f1f7600819..f0c961cc89 100644 --- a/test/WebSites/TagHelpersWebSite/Views/Employee/Create.cshtml +++ b/test/WebSites/TagHelpersWebSite/Views/Employee/Create.cshtml @@ -22,7 +22,7 @@

Employee


-
+