From cd138a99ea6422ead3d1f65083229f187441cf0f Mon Sep 17 00:00:00 2001 From: Doug Bunting Date: Sun, 7 Jun 2015 22:45:53 -0700 Subject: [PATCH] Demonstrate #1485, #1487, #2662, #2664 in functional and unit tests - test additional cases _close_ to these bugs as well for #1485 - show odd `@Html.CheckBox()`, `@Html.Hidden()` behaviour in unit tests - show odd `@Html.TextBox()` behaviour in functional tests (templates) for #1487 - show odd `@Html.Value()` behaviour in unit tests - show odd `@Html.RadioButton()`, `@Html.TextArea()` behaviour in functional tests - show lack of validation attributes for `@Html.RadioButton()`, `"; @@ -121,6 +121,170 @@ public void CheckBoxUsesAttemptedValueFromModelState() Assert.Equal(expected, html.ToString()); } + [Fact] + public void CheckBoxNotInTemplate_GetsValueFromViewDataDictionary() + { + // Arrange + var expected = + @"" + + @""; + var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetTestModelViewData()); + helper.ViewContext.ClientValidationEnabled = false; + helper.ViewData.Model = new TestModel(); + + // Act + var html = helper.CheckBox("Property1", isChecked: null, htmlAttributes: null); + + // Assert + Assert.Equal(expected, html.ToString()); + } + + [Fact] + public void CheckBoxInTemplate_GetsValueFromViewDataDictionary() + { + // Arrange + var expected = + @"" + + @""; + var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetTestModelViewData()); + helper.ViewContext.ClientValidationEnabled = false; + helper.ViewData.Remove(nameof(TestModel.Property1)); + helper.ViewData["Prefix.Property1"] = true; + helper.ViewData.Model = new TestModel(); + helper.ViewData.TemplateInfo.HtmlFieldPrefix = "Prefix"; + + // Act + var html = helper.CheckBox("Property1", isChecked: null, htmlAttributes: null); + + // Assert + Assert.Equal(expected, html.ToString()); + } + + [Fact] + public void CheckBoxNotInTemplate_GetsValueFromPropertyOfViewDataEntry() + { + // Arrange + var expected = + @"" + + @""; + var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetTestModelViewData()); + helper.ViewContext.ClientValidationEnabled = false; + helper.ViewData.Remove(nameof(TestModel.Property1)); + helper.ViewData["Prefix"] = new TestModel { Property1 = true }; + helper.ViewData.Model = new TestModel(); + + // Act + var html = helper.CheckBox("Prefix.Property1", isChecked: null, htmlAttributes: null); + + // Assert + Assert.Equal(expected, html.ToString()); + } + + [Fact] + public void CheckBoxInTemplate_GetsValueFromPropertyOfViewDataEntry() + { + // Arrange + var expected = + @"" + + @""; + var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetTestModelViewData()); + helper.ViewContext.ClientValidationEnabled = false; + helper.ViewData.Remove(nameof(TestModel.Property1)); + helper.ViewData["Prefix"] = new TestModel { Property1 = true }; + helper.ViewData.Model = new TestModel(); + helper.ViewData.TemplateInfo.HtmlFieldPrefix = "Prefix"; + + // Act + var html = helper.CheckBox("Property1", isChecked: null, htmlAttributes: null); + + // Assert + Assert.Equal(expected, html.ToString()); + } + + [Fact] + public void CheckBoxNotInTemplate_GetsModelValue_IfModelStateAndViewDataEmpty() + { + // Arrange + var expected = + @"" + + @""; + var metadataProvider = new EmptyModelMetadataProvider(); + var helper = DefaultTemplatesUtilities.GetHtmlHelper(new ViewDataDictionary(metadataProvider)); + helper.ViewContext.ClientValidationEnabled = false; + helper.ViewData.Model = new TestModel { Property1 = true }; + + // Act + var html = helper.CheckBox("Property1", isChecked: null, htmlAttributes: null); + + // Assert + Assert.Equal(expected, html.ToString()); + } + + [Fact(Skip = "#1485, unable to get Model value.")] + public void CheckBoxInTemplate_GetsModelValue_IfModelStateAndViewDataEmpty() + { + // Arrange + var expected = + @"" + + @""; + var metadataProvider = new EmptyModelMetadataProvider(); + var helper = DefaultTemplatesUtilities.GetHtmlHelper(new ViewDataDictionary(metadataProvider)); + helper.ViewContext.ClientValidationEnabled = false; + helper.ViewData.Model = new TestModel { Property1 = true }; + helper.ViewData.TemplateInfo.HtmlFieldPrefix = "Prefix"; + + // Act + var html = helper.CheckBox("Property1", isChecked: null, htmlAttributes: null); + + // Assert + Assert.Equal(expected, html.ToString()); + } + + [Fact] + public void CheckBoxNotInTemplate_NotChecked_IfPropertyIsNotFound() + { + // Arrange + var expected = + @"" + + @""; + var metadataProvider = new EmptyModelMetadataProvider(); + var helper = DefaultTemplatesUtilities.GetHtmlHelper(new ViewDataDictionary(metadataProvider)); + helper.ViewContext.ClientValidationEnabled = false; + + // Act + var html = helper.CheckBox("Property1", isChecked: null, htmlAttributes: null); + + // Assert + Assert.Equal(expected, html.ToString()); + } + + [Fact] + public void CheckBoxInTemplate_NotChecked_IfPropertyIsNotFound() + { + // Arrange + var expected = + @"" + + @""; + var metadataProvider = new EmptyModelMetadataProvider(); + var helper = DefaultTemplatesUtilities.GetHtmlHelper(new ViewDataDictionary(metadataProvider)); + helper.ViewContext.ClientValidationEnabled = false; + helper.ViewData.TemplateInfo.HtmlFieldPrefix = "Prefix"; + + // Act + var html = helper.CheckBox("Property1", isChecked: null, htmlAttributes: null); + + // Assert + Assert.Equal(expected, html.ToString()); + } + [Fact] public void CheckBoxGeneratesUnobtrusiveValidationAttributes() { @@ -142,9 +306,9 @@ public void CheckBoxReplacesUnderscoresInHtmlAttributesWithDashes() { // Arrange var expected = - @""; var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetTestModelViewData()); @@ -158,7 +322,7 @@ public void CheckBoxReplacesUnderscoresInHtmlAttributesWithDashes() } [Fact] - public void CheckBoxWithPrefix_ReplaceDotsInIdByDefaultWithUnderscores() + public void CheckBoxInTemplate_ReplaceDotsInIdByDefaultWithUnderscores() { // Arrange var expected = @" m.Property1); + var html = helper.CheckBoxFor(m => m.Property1, htmlAttributes: null); // Assert Assert.Equal(expected, html.ToString()); @@ -275,7 +439,7 @@ public void CheckBoxForWithNonNullContainer_UsesPropertyValue(bool value, string var helper = DefaultTemplatesUtilities.GetHtmlHelper(viewData); // Act - var html = helper.CheckBoxFor(m => m.Property1); + var html = helper.CheckBoxFor(m => m.Property1, htmlAttributes: null); // Assert Assert.Equal(expected, html.ToString()); @@ -286,7 +450,7 @@ public void CheckBoxForOverridesCalculatedParametersWithValuesFromHtmlAttributes { // Arrange var expected = - @""; @@ -326,7 +490,7 @@ public void CheckBoxForGeneratesUnobtrusiveValidationAttributes() public void CheckBoxFor_UsesModelStateAttemptedValue(string attemptedValue, string expectedChecked) { // Arrange - var expected = + var expected = @"" + @""; @@ -339,7 +503,7 @@ public void CheckBoxFor_UsesModelStateAttemptedValue(string attemptedValue, stri viewData.ModelState.SetModelValue("Property1", valueProviderResult); // Act - var html = helper.CheckBoxFor(m => m.Property1); + var html = helper.CheckBoxFor(m => m.Property1, htmlAttributes: null); // Assert Assert.Equal(expected, html.ToString()); @@ -365,10 +529,10 @@ public void CheckBoxFor_WithObjectAttribute_MapsUnderscoresInNamesToDashes() } [Fact] - public void CheckBoxForWith_AttributeDictionary_GeneratesExpectedAttributes() + public void CheckBoxFor_WithAttributeDictionary_GeneratesExpectedAttributes() { // Arrange - var expected = + var expected = @""; + var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetViewDataWithNonNullModel()); + helper.ViewData.Model.Property1 = "model-property1-value"; + helper.ViewData["Prefix"] = new HiddenModel { Property1 = "contained-view-data-value" }; + + // Act + var html = helper.Hidden("Prefix.Property1", value: null, htmlAttributes: null); + + // Assert + Assert.Equal(expected, html.ToString()); + } + + [Fact] + public void HiddenInTemplate_GetsValueFromPropertyOfViewDataEntry() + { + // Arrange + var expected = @""; + var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetViewDataWithNonNullModel()); + helper.ViewData.TemplateInfo.HtmlFieldPrefix = "Prefix"; + helper.ViewData.Model.Property1 = "model-property1-value"; + helper.ViewData["Prefix"] = new HiddenModel { Property1 = "contained-view-data-value" }; + + // Act + var html = helper.Hidden("Property1", value: null, htmlAttributes: null); + + // Assert + Assert.Equal(expected, html.ToString()); + } + + [Fact] + public void HiddenNotInTemplate_GetsValueFromViewDataEntry_EvenIfNull() + { + // Arrange + var expected = @""; + var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetViewDataWithNonNullModel()); + helper.ViewData.Model.Property1 = "model-property1-value"; + helper.ViewData["Property1"] = null; + + // Act + var html = helper.Hidden("Property1", value: null, htmlAttributes: null); + + // Assert + Assert.Equal(expected, html.ToString()); + } + + [Fact] + public void HiddenInTemplate_GetsValueFromViewDataEntry_EvenIfNull() + { + // Arrange + var expected = @""; + var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetViewDataWithNonNullModel()); + helper.ViewData.TemplateInfo.HtmlFieldPrefix = "Prefix"; + helper.ViewData.Model.Property1 = "model-property1-value"; + helper.ViewData["Prefix.Property1"] = null; + + // Act + var html = helper.Hidden("Property1", value: null, htmlAttributes: null); + + // Assert + Assert.Equal(expected, html.ToString()); + } + [Fact] public void HiddenOverridesValueFromAttributesWithArgumentValue() { @@ -92,7 +162,7 @@ public void HiddenWithArgumentValueAndNullModel_UsesArgumentValue() } [Fact] - public void HiddenWithNullValueAndNullModel_GeneratesExpectedValue() + public void HiddenWithNonNullValue_GeneratesExpectedValue() { // Arrange var expected = @""; - var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetViewDataWithModelStateAndModelAndViewDataValues()); - helper.ViewData.ModelState.Clear(); - helper.ViewData.Clear(); + var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetViewDataWithNonNullModel()); helper.ViewData.Model.Property1 = "property-value"; // Act @@ -171,15 +239,29 @@ public void HiddenUsesPropertyValue_IfModelStateAndViewDataDoNotHavePropertyAndE Assert.Equal(expected, result.ToString()); } + [Fact(Skip = "#1485, unable to get Model value.")] + public void HiddenInTemplate_GetsModelValue_IfModelStateAndViewDataEmpty() + { + // Arrange + var expected = @""; + var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetViewDataWithNonNullModel()); + helper.ViewData.TemplateInfo.HtmlFieldPrefix = "Prefix"; + helper.ViewData.Model.Property1 = "property-value"; + + // Act + var html = helper.Hidden("Property1", value: null, htmlAttributes: new { value = "attribute-value" }); + + // Assert + Assert.Equal(expected, html.ToString()); + } + [Fact] - public void HiddenDoesNotUsesAttributeValue() + public void HiddenNotInTemplate_DoesNotUseAttributeValue() { // Arrange var expected = @""; - var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetViewDataWithModelStateAndModelAndViewDataValues()); - helper.ViewData.ModelState.Clear(); - helper.ViewData.Clear(); - helper.ViewData.Model.Property1 = null; + var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetViewDataWithNonNullModel()); // Act var result = helper.Hidden("Property1", value: null, htmlAttributes: new { value = "attribute-value" }); @@ -189,7 +271,23 @@ public void HiddenDoesNotUsesAttributeValue() } [Fact] - public void HiddenReturnsEmptyValue_IfPropertyIsNotFound() + public void HiddenInTemplate_DoesNotUseAttributeValue() + { + // Arrange + var expected = @""; + var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetViewDataWithNonNullModel()); + helper.ViewData.TemplateInfo.HtmlFieldPrefix = "Prefix"; + + // Act + var html = helper.Hidden("Property1", value: null, htmlAttributes: new { value = "attribute-value" }); + + // Assert + Assert.Equal(expected, html.ToString()); + } + + [Fact] + public void HiddenNotInTemplate_GetsEmptyValue_IfPropertyIsNotFound() { // Arrange var expected = @""; + var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetViewDataWithModelStateAndModelAndViewDataValues()); + helper.ViewData.TemplateInfo.HtmlFieldPrefix = "Prefix"; + + // Act + var html = helper.Hidden("keyNotFound", value: null, htmlAttributes: null); + + // Assert + Assert.Equal(expected, html.ToString()); + } + + [Fact] + public void HiddenInTemplate_WithExplicitValue_GeneratesExpectedValue() { // Arrange var expected = @""; @@ -236,7 +350,7 @@ public void HiddenWithPrefixAndEmptyName_GeneratesExpectedValue() } [Fact] - public void HiddenUsesPrefixName_ToLookupPropertyValueInModelState() + public void HiddenInTemplate_UsesPrefixName_ToLookupPropertyValueInModelState() { // Arrange var expected = @" m.Property1); + var result = helper.HiddenFor(m => m.Property1, htmlAttributes: null); // Assert Assert.Equal(expected, result.ToString()); } [Fact] - public void HiddenForWithPrefix_UsesPrefixWhenLookingUpModelStateValues() + public void HiddenForInTemplate_UsesPrefixWhenLookingUpModelStateValues() { // Arrange var expected = @" m.Property1); + var result = helper.HiddenFor(m => m.Property1, htmlAttributes: null); // Assert Assert.Equal(expected, result.ToString()); @@ -671,7 +785,7 @@ public static TheoryData HiddenFor_UsesModelStateValueForComplexExpressionsData [Theory] [MemberData(nameof(HiddenFor_UsesModelStateValueForComplexExpressionsData))] - public void HiddenFor_UsesModelStateValueForComplexExpressions( + public void HiddenForInTemplate_UsesModelStateValueForComplexExpressions( Expression> expression, string expected) { @@ -718,13 +832,20 @@ private static ViewDataDictionary GetViewDataWithNullModelAndNonNul }; } - private static ViewDataDictionary GetViewDataWithModelStateAndModelAndViewDataValues() + private static ViewDataDictionary GetViewDataWithNonNullModel() { var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider()) { Model = new HiddenModel(), - ["Property1"] = "view-data-val", }; + + return viewData; + } + + private static ViewDataDictionary GetViewDataWithModelStateAndModelAndViewDataValues() + { + var viewData = GetViewDataWithNonNullModel(); + viewData["Property1"] = "view-data-val"; viewData.ModelState.Add("Property1", GetModelState("ModelStateValue")); return viewData; diff --git a/test/Microsoft.AspNet.Mvc.Extensions.Test/Rendering/HtmlHelperSelectTest.cs b/test/Microsoft.AspNet.Mvc.Extensions.Test/Rendering/HtmlHelperSelectTest.cs index a868e3cce9..7c433349a9 100644 --- a/test/Microsoft.AspNet.Mvc.Extensions.Test/Rendering/HtmlHelperSelectTest.cs +++ b/test/Microsoft.AspNet.Mvc.Extensions.Test/Rendering/HtmlHelperSelectTest.cs @@ -59,6 +59,17 @@ public class HtmlHelperSelectTest new SelectListItem { Group = DisabledGroup, Selected = true, Text = "Two", Value = "2"}, new SelectListItem { Group = DisabledGroup, Selected = true, Text = "Three", Value = "3"}, }; + private static readonly List SourcesSelectList = new List + { + new SelectListItem { Text = SelectSources.ModelStateEntry.ToString() }, + new SelectListItem { Text = SelectSources.ModelStateEntryWithPrefix.ToString() }, + new SelectListItem { Text = SelectSources.ViewDataEntry.ToString() }, + new SelectListItem { Text = SelectSources.PropertyOfViewDataEntry.ToString() }, + new SelectListItem { Text = SelectSources.ViewDataEntryWithPrefix.ToString() }, + new SelectListItem { Text = SelectSources.PropertyOfViewDataEntryWithPrefix.ToString() }, + new SelectListItem { Text = SelectSources.ModelValue.ToString() }, + new SelectListItem { Text = SelectSources.PropertyOfModel.ToString() }, + }; // Select list -> expected HTML with null model, expected HTML with model containing "2". public static TheoryData, string, string> DropDownListDataSet @@ -371,6 +382,200 @@ public class HtmlHelperSelectTest Assert.Equal(savedSelected, selectList.Select(item => item.Selected)); } + [Fact] + public void DropDownListNotInTemplate_GetsModelStateEntry() + { + // Arrange + var expectedHtml = GetExpectedSelectElement(SelectSources.ModelStateEntry, allowMultiple: false); + + var entryResult = new ValueProviderResult( + SelectSources.ModelStateEntry, + SelectSources.ModelStateEntry.ToString(), + culture: null); + var entryResultWithPrefix = new ValueProviderResult( + SelectSources.ModelStateEntryWithPrefix, + SelectSources.ModelStateEntryWithPrefix.ToString(), + culture: null); + var modelState = new ModelStateDictionary + { + ["Property1"] = new ModelState { Value = entryResult }, + ["Prefix.Property1"] = new ModelState { Value = entryResultWithPrefix }, + }; + + var provider = TestModelMetadataProvider.CreateDefaultProvider(); + var viewData = new ViewDataDictionary(provider, modelState) + { + ["Property1"] = SelectSources.ViewDataEntry, + ["Prefix.Property1"] = SelectSources.ViewDataEntryWithPrefix, + ["Prefix"] = new ModelContainingSources { Property1 = SelectSources.PropertyOfViewDataEntry }, + }; + viewData.Model = new ModelContainingSources { Property1 = SelectSources.PropertyOfModel }; + + var helper = DefaultTemplatesUtilities.GetHtmlHelper(viewData); + helper.ViewContext.ClientValidationEnabled = false; + + // Act + var html = helper.DropDownList("Property1", SourcesSelectList, optionLabel: null, htmlAttributes: null); + + // Assert + Assert.Equal(expectedHtml, html.ToString()); + } + + [Fact] + public void DropDownListInTemplate_GetsModelStateEntry() + { + // Arrange + var expectedHtml = GetExpectedSelectElementWithPrefix( + SelectSources.ModelStateEntryWithPrefix, + allowMultiple: false); + + var entryResult = new ValueProviderResult( + SelectSources.ModelStateEntry, + SelectSources.ModelStateEntry.ToString(), + culture: null); + var entryResultWithPrefix = new ValueProviderResult( + SelectSources.ModelStateEntryWithPrefix, + SelectSources.ModelStateEntryWithPrefix.ToString(), + culture: null); + var modelState = new ModelStateDictionary + { + ["Property1"] = new ModelState { Value = entryResult }, + ["Prefix.Property1"] = new ModelState { Value = entryResultWithPrefix }, + }; + + var provider = TestModelMetadataProvider.CreateDefaultProvider(); + var viewData = new ViewDataDictionary(provider, modelState) + { + ["Property1"] = SelectSources.ViewDataEntry, + ["Prefix.Property1"] = SelectSources.ViewDataEntryWithPrefix, + ["Prefix"] = new ModelContainingSources { Property1 = SelectSources.PropertyOfViewDataEntry }, + }; + viewData.Model = new ModelContainingSources { Property1 = SelectSources.PropertyOfModel }; + viewData.TemplateInfo.HtmlFieldPrefix = "Prefix"; + + var helper = DefaultTemplatesUtilities.GetHtmlHelper(viewData); + helper.ViewContext.ClientValidationEnabled = false; + + // Act + var html = helper.DropDownList("Property1", SourcesSelectList, optionLabel: null, htmlAttributes: null); + + // Assert + Assert.Equal(expectedHtml, html.ToString()); + } + + [Fact] + public void DropDownListNotInTemplate_GetsViewDataEntry_IfModelStateEmpty() + { + // Arrange + var expectedHtml = GetExpectedSelectElement(SelectSources.ViewDataEntry, allowMultiple: false); + + var provider = TestModelMetadataProvider.CreateDefaultProvider(); + var viewData = new ViewDataDictionary(provider) + { + ["Property1"] = SelectSources.ViewDataEntry, + ["Prefix.Property1"] = SelectSources.ViewDataEntryWithPrefix, + ["Prefix"] = new ModelContainingSources { Property1 = SelectSources.PropertyOfViewDataEntry }, + }; + viewData.Model = new ModelContainingSources { Property1 = SelectSources.PropertyOfModel }; + + var helper = DefaultTemplatesUtilities.GetHtmlHelper(viewData); + helper.ViewContext.ClientValidationEnabled = false; + + // Act + var html = helper.DropDownList("Property1", SourcesSelectList, optionLabel: null, htmlAttributes: null); + + // Assert + Assert.Equal(expectedHtml, html.ToString()); + } + + [Fact(Skip = "#1487, incorrectly matches Property1 entry (without prefix) in ViewData.")] + public void DropDownListInTemplate_GetsViewDataEntry_IfModelStateEmpty() + { + // Arrange + var expectedHtml = GetExpectedSelectElementWithPrefix( + SelectSources.ViewDataEntryWithPrefix, + allowMultiple: false); + + var provider = TestModelMetadataProvider.CreateDefaultProvider(); + var viewData = new ViewDataDictionary(provider) + { + ["Property1"] = SelectSources.ViewDataEntry, + ["Prefix.Property1"] = SelectSources.ViewDataEntryWithPrefix, + ["Prefix"] = new ModelContainingSources { Property1 = SelectSources.PropertyOfViewDataEntry }, + }; + viewData.Model = new ModelContainingSources { Property1 = SelectSources.PropertyOfModel }; + viewData.TemplateInfo.HtmlFieldPrefix = "Prefix"; + + var helper = DefaultTemplatesUtilities.GetHtmlHelper(viewData); + helper.ViewContext.ClientValidationEnabled = false; + + // Act + var html = helper.DropDownList("Property1", SourcesSelectList, optionLabel: null, htmlAttributes: null); + + // Assert + Assert.Equal(expectedHtml, html.ToString()); + } + + [Fact(Skip = "#1487, incorrectly matches Property1 entry (without prefix) in ViewData.")] + public void DropDownListInTemplate_GetsPropertyOfViewDataEntry_IfModelStateEmptyAndNoViewDataEntryWithPrefix() + { + // Arrange + var expectedHtml = GetExpectedSelectElementWithPrefix( + SelectSources.PropertyOfViewDataEntry, + allowMultiple: false); + + var provider = TestModelMetadataProvider.CreateDefaultProvider(); + var viewData = new ViewDataDictionary(provider) + { + ["Property1"] = SelectSources.ViewDataEntry, + ["Prefix"] = new ModelContainingSources { Property1 = SelectSources.PropertyOfViewDataEntry }, + }; + viewData.Model = new ModelContainingSources { Property1 = SelectSources.PropertyOfModel }; + viewData.TemplateInfo.HtmlFieldPrefix = "Prefix"; + + var helper = DefaultTemplatesUtilities.GetHtmlHelper(viewData); + helper.ViewContext.ClientValidationEnabled = false; + + // Act + var html = helper.DropDownList("Property1", SourcesSelectList, optionLabel: null, htmlAttributes: null); + + // Assert + Assert.Equal(expectedHtml, html.ToString()); + } + + [Fact] + public void DropDownListNotInTemplate_GetsPropertyOfModel_IfModelStateAndViewDataEmpty() + { + // Arrange + var expectedHtml = GetExpectedSelectElement(SelectSources.PropertyOfModel, allowMultiple: false); + var model = new ModelContainingSources { Property1 = SelectSources.PropertyOfModel }; + var helper = DefaultTemplatesUtilities.GetHtmlHelper(model); + helper.ViewContext.ClientValidationEnabled = false; + + // Act + var html = helper.DropDownList("Property1", SourcesSelectList, optionLabel: null, htmlAttributes: null); + + // Assert + Assert.Equal(expectedHtml, html.ToString()); + } + + [Fact] + public void DropDownListInTemplate_GetsPropertyOfModel_IfModelStateAndViewDataEmpty() + { + // Arrange + var expectedHtml = GetExpectedSelectElementWithPrefix(SelectSources.PropertyOfModel, allowMultiple: false); + var model = new ModelContainingSources { Property1 = SelectSources.PropertyOfModel }; + var helper = DefaultTemplatesUtilities.GetHtmlHelper(model); + helper.ViewContext.ClientValidationEnabled = false; + helper.ViewData.TemplateInfo.HtmlFieldPrefix = "Prefix"; + + // Act + var html = helper.DropDownList("Property1", SourcesSelectList, optionLabel: null, htmlAttributes: null); + + // Assert + Assert.Equal(expectedHtml, html.ToString()); + } + [Theory] [MemberData(nameof(DropDownListDataSet))] public void DropDownListFor_WithNullModel_GeneratesExpectedValue( @@ -588,6 +793,200 @@ public void DropDownListFor_WithUnrelatedExpression_GeneratesExpectedValue() Assert.Equal(savedSelected, selectList.Select(item => item.Selected)); } + [Fact] + public void ListBoxNotInTemplate_GetsModelStateEntry() + { + // Arrange + var expectedHtml = GetExpectedSelectElement(SelectSources.ModelStateEntry, allowMultiple: true); + + var entryResult = new ValueProviderResult( + SelectSources.ModelStateEntry, + SelectSources.ModelStateEntry.ToString(), + culture: null); + var entryResultWithPrefix = new ValueProviderResult( + SelectSources.ModelStateEntryWithPrefix, + SelectSources.ModelStateEntryWithPrefix.ToString(), + culture: null); + var modelState = new ModelStateDictionary + { + ["Property1"] = new ModelState { Value = entryResult }, + ["Prefix.Property1"] = new ModelState { Value = entryResultWithPrefix }, + }; + + var provider = TestModelMetadataProvider.CreateDefaultProvider(); + var viewData = new ViewDataDictionary(provider, modelState) + { + ["Property1"] = new[] { SelectSources.ViewDataEntry }, + ["Prefix.Property1"] = new[] { SelectSources.ViewDataEntryWithPrefix }, + ["Prefix"] = new ModelContainingListOfSources { Property1 = { SelectSources.PropertyOfViewDataEntry } }, + }; + viewData.Model = new ModelContainingListOfSources { Property1 = { SelectSources.PropertyOfModel } }; + + var helper = DefaultTemplatesUtilities.GetHtmlHelper(viewData); + helper.ViewContext.ClientValidationEnabled = false; + + // Act + var html = helper.ListBox("Property1", SourcesSelectList, htmlAttributes: null); + + // Assert + Assert.Equal(expectedHtml, html.ToString()); + } + + [Fact] + public void ListBoxInTemplate_GetsModelStateEntry() + { + // Arrange + var expectedHtml = GetExpectedSelectElementWithPrefix( + SelectSources.ModelStateEntryWithPrefix, + allowMultiple: true); + + var entryResult = new ValueProviderResult( + SelectSources.ModelStateEntry, + SelectSources.ModelStateEntry.ToString(), + culture: null); + var entryResultWithPrefix = new ValueProviderResult( + SelectSources.ModelStateEntryWithPrefix, + SelectSources.ModelStateEntryWithPrefix.ToString(), + culture: null); + var modelState = new ModelStateDictionary + { + ["Property1"] = new ModelState { Value = entryResult }, + ["Prefix.Property1"] = new ModelState { Value = entryResultWithPrefix }, + }; + + var provider = TestModelMetadataProvider.CreateDefaultProvider(); + var viewData = new ViewDataDictionary(provider, modelState) + { + ["Property1"] = new[] { SelectSources.ViewDataEntry }, + ["Prefix.Property1"] = new[] { SelectSources.ViewDataEntryWithPrefix }, + ["Prefix"] = new ModelContainingListOfSources { Property1 = { SelectSources.PropertyOfViewDataEntry } }, + }; + viewData.Model = new ModelContainingListOfSources { Property1 = { SelectSources.PropertyOfModel } }; + viewData.TemplateInfo.HtmlFieldPrefix = "Prefix"; + + var helper = DefaultTemplatesUtilities.GetHtmlHelper(viewData); + helper.ViewContext.ClientValidationEnabled = false; + + // Act + var html = helper.ListBox("Property1", SourcesSelectList, htmlAttributes: null); + + // Assert + Assert.Equal(expectedHtml, html.ToString()); + } + + [Fact(Skip = "#2664, throws ArgumentNullException")] + public void ListBoxNotInTemplate_GetsViewDataEntry_IfModelStateEmpty() + { + // Arrange + var expectedHtml = GetExpectedSelectElement(SelectSources.ViewDataEntry, allowMultiple: true); + + var provider = TestModelMetadataProvider.CreateDefaultProvider(); + var viewData = new ViewDataDictionary(provider) + { + ["Property1"] = new[] { SelectSources.ViewDataEntry }, + ["Prefix.Property1"] = new[] { SelectSources.ViewDataEntryWithPrefix }, + ["Prefix"] = new ModelContainingListOfSources { Property1 = { SelectSources.PropertyOfViewDataEntry } }, + }; + viewData.Model = new ModelContainingListOfSources { Property1 = { SelectSources.PropertyOfModel } }; + + var helper = DefaultTemplatesUtilities.GetHtmlHelper(viewData); + helper.ViewContext.ClientValidationEnabled = false; + + // Act + var html = helper.ListBox("Property1", SourcesSelectList, htmlAttributes: null); + + // Assert + Assert.Equal(expectedHtml, html.ToString()); + } + + [Fact(Skip = "#2664, throws ArgumentNullException")] + public void ListBoxInTemplate_GetsViewDataEntry_IfModelStateEmpty() + { + // Arrange + var expectedHtml = GetExpectedSelectElementWithPrefix( + SelectSources.ViewDataEntryWithPrefix, + allowMultiple: true); + + var provider = TestModelMetadataProvider.CreateDefaultProvider(); + var viewData = new ViewDataDictionary(provider) + { + ["Property1"] = new[] { SelectSources.ViewDataEntry }, + ["Prefix.Property1"] = new[] { SelectSources.ViewDataEntryWithPrefix }, + ["Prefix"] = new ModelContainingListOfSources { Property1 = { SelectSources.PropertyOfViewDataEntry } }, + }; + viewData.Model = new ModelContainingListOfSources { Property1 = { SelectSources.PropertyOfModel } }; + viewData.TemplateInfo.HtmlFieldPrefix = "Prefix"; + + var helper = DefaultTemplatesUtilities.GetHtmlHelper(viewData); + helper.ViewContext.ClientValidationEnabled = false; + + // Act + var html = helper.ListBox("Property1", SourcesSelectList, htmlAttributes: null); + + // Assert + Assert.Equal(expectedHtml, html.ToString()); + } + + [Fact(Skip = "#2664, throws ArgumentNullException")] + public void ListBoxInTemplate_GetsPropertyOfViewDataEntry_IfModelStateEmptyAndNoViewDataEntryWithPrefix() + { + // Arrange + var expectedHtml = GetExpectedSelectElementWithPrefix( + SelectSources.PropertyOfViewDataEntry, + allowMultiple: true); + + var provider = TestModelMetadataProvider.CreateDefaultProvider(); + var viewData = new ViewDataDictionary(provider) + { + ["Property1"] = new[] { SelectSources.ViewDataEntry }, + ["Prefix"] = new ModelContainingListOfSources { Property1 = { SelectSources.PropertyOfViewDataEntry } }, + }; + viewData.Model = new ModelContainingListOfSources { Property1 = { SelectSources.PropertyOfModel } }; + viewData.TemplateInfo.HtmlFieldPrefix = "Prefix"; + + var helper = DefaultTemplatesUtilities.GetHtmlHelper(viewData); + helper.ViewContext.ClientValidationEnabled = false; + + // Act + var html = helper.ListBox("Property1", SourcesSelectList, htmlAttributes: null); + + // Assert + Assert.Equal(expectedHtml, html.ToString()); + } + + [Fact(Skip = "#2664, throws ArgumentNullException")] + public void ListBoxNotInTemplate_GetsPropertyOfModel_IfModelStateAndViewDataEmpty() + { + // Arrange + var expectedHtml = GetExpectedSelectElement(SelectSources.PropertyOfModel, allowMultiple: true); + var model = new ModelContainingListOfSources { Property1 = { SelectSources.PropertyOfModel } }; + var helper = DefaultTemplatesUtilities.GetHtmlHelper(model); + helper.ViewContext.ClientValidationEnabled = false; + + // Act + var html = helper.ListBox("Property1", SourcesSelectList, htmlAttributes: null); + + // Assert + Assert.Equal(expectedHtml, html.ToString()); + } + + [Fact(Skip = "#2664, throws ArgumentNullException")] + public void ListBoxInTemplate_GetsPropertyOfModel_IfModelStateAndViewDataEmpty() + { + // Arrange + var expectedHtml = GetExpectedSelectElementWithPrefix(SelectSources.PropertyOfModel, allowMultiple: true); + var model = new ModelContainingListOfSources { Property1 = { SelectSources.PropertyOfModel } }; + var helper = DefaultTemplatesUtilities.GetHtmlHelper(model); + helper.ViewContext.ClientValidationEnabled = false; + helper.ViewData.TemplateInfo.HtmlFieldPrefix = "Prefix"; + + // Act + var html = helper.ListBox("Property1", SourcesSelectList, htmlAttributes: null); + + // Assert + Assert.Equal(expectedHtml, html.ToString()); + } + [Theory] [MemberData(nameof(ListBoxDataSet))] public void ListBoxFor_WithNullModel_GeneratesExpectedValue( @@ -1072,6 +1471,51 @@ public void GetEnumSelectList_ReturnsExpectedItems(Type type, IEnumerable"; + } + + private static string GetExpectedSelectElementWithPrefix(SelectSources source, bool allowMultiple) + { + return $""; + } + + private static string GetMultiple(bool allowMultiple) + { + return allowMultiple ? " multiple=\"HtmlEncode[[multiple]]\"" : string.Empty; + } + + private static string GetOption(SelectSources optionSource, SelectSources source) + { + return $"HtmlEncode[[{ optionSource.ToString() }]]"; + } + + private static string GetSelected(SelectSources optionSource, SelectSources source) + { + return optionSource == source ? " selected=\"HtmlEncode[[selected]]\"" : string.Empty; + } + // Confirm methods that wrap GetEnumSelectList(ModelMetadata) are not changing anything in returned collection. private void VerifySelectList(IEnumerable expected, IEnumerable actual) { @@ -1137,6 +1581,28 @@ protected override IEnumerable GetEnumSelectList([NotNull] Model } } + private enum SelectSources + { + ModelStateEntry, + ModelStateEntryWithPrefix, + ViewDataEntry, + PropertyOfViewDataEntry, + ViewDataEntryWithPrefix, + PropertyOfViewDataEntryWithPrefix, + ModelValue, + PropertyOfModel, + }; + + private class ModelContainingSources + { + public SelectSources Property1 { get; set; } + } + + private class ModelContainingListOfSources + { + public List Property1 { get; } = new List(); + } + private class ClassWithFields { public const int Zero = 0; diff --git a/test/Microsoft.AspNet.Mvc.Extensions.Test/Rendering/HtmlHelperValueExtensionsTest.cs b/test/Microsoft.AspNet.Mvc.Extensions.Test/Rendering/HtmlHelperValueTest.cs similarity index 59% rename from test/Microsoft.AspNet.Mvc.Extensions.Test/Rendering/HtmlHelperValueExtensionsTest.cs rename to test/Microsoft.AspNet.Mvc.Extensions.Test/Rendering/HtmlHelperValueTest.cs index 846c9b9a28..b95624f73e 100644 --- a/test/Microsoft.AspNet.Mvc.Extensions.Test/Rendering/HtmlHelperValueExtensionsTest.cs +++ b/test/Microsoft.AspNet.Mvc.Extensions.Test/Rendering/HtmlHelperValueTest.cs @@ -11,25 +11,127 @@ namespace Microsoft.AspNet.Mvc.Core { /// - /// Test the class. + /// Test the and methods. /// - public class HtmlHelperValueExtensionsTest + public class HtmlHelperValueTest { // Value [Fact] - public void ValueGetsValueFromViewData() + public void ValueNotInTemplate_GetsValueFromViewData() { // Arrange var helper = GetHtmlHelper(); // Act - var html = helper.Value("StringProperty"); + var html = helper.Value("StringProperty", format: null); // Assert Assert.Equal("ViewDataValue", html); } + [Fact(Skip = "$1487, finds 'StringProperty' entry (without prefix) instead.")] + public void ValueInTemplate_GetsValueFromPrefixedViewDataEntry() + { + // Arrange + var helper = GetHtmlHelper(); + helper.ViewData["Prefix.StringProperty"] = "PrefixedViewDataValue"; + helper.ViewData.TemplateInfo.HtmlFieldPrefix = "Prefix"; + + // Act + var html = helper.Value("StringProperty", format: null); + + // Assert + Assert.Equal("PrefixedViewDataValue", html); + } + + [Fact] + public void ValueNotInTemplate_GetsValueFromPropertyOfViewDataEntry() + { + // Arrange + var helper = GetHtmlHelper(); + helper.ViewData["Prefix"] = new { StringProperty = "ContainedViewDataValue" }; + + // Act + var html = helper.Value("Prefix.StringProperty", format: null); + + // Assert + Assert.Equal("ContainedViewDataValue", html); + } + + [Fact(Skip = "$1487, finds 'StringProperty' entry (without prefix) instead.")] + public void ValueInTemplate_GetsValueFromPropertyOfViewDataEntry() + { + // Arrange + var helper = GetHtmlHelper(); + helper.ViewData["Prefix"] = new { StringProperty = "ContainedViewDataValue" }; + helper.ViewData.TemplateInfo.HtmlFieldPrefix = "Prefix"; + + // Act + var html = helper.Value("StringProperty", format: null); + + // Assert + Assert.Equal("ContainedViewDataValue", html); + } + + [Fact] + public void ValueNotInTemplate_GetsValueFromModel_IfNoViewDataEntry() + { + // Arrange + var helper = GetHtmlHelper(); + helper.ViewData.Clear(); + + // Act + var html = helper.Value("StringProperty", format: null); + + // Assert + Assert.Equal("ModelStringPropertyValue", html); + } + + [Fact] + public void ValueInTemplate_GetsValueFromModel_IfNoViewDataEntry() + { + // Arrange + var helper = GetHtmlHelper(); + helper.ViewData.Clear(); + helper.ViewData.TemplateInfo.HtmlFieldPrefix = "Prefix"; + + // Act + var html = helper.Value("StringProperty", format: null); + + // Assert + Assert.Equal("ModelStringPropertyValue", html); + } + + [Fact] + public void ValueNotInTemplate_GetsValueFromViewData_EvenIfNull() + { + // Arrange + var helper = GetHtmlHelper(); + helper.ViewData["StringProperty"] = null; + + // Act + var html = helper.Value("StringProperty", format: null); + + // Assert + Assert.Empty(html); + } + + [Fact(Skip = "$1487, finds 'StringProperty' entry (without prefix) instead.")] + public void ValueInTemplate_GetsValueFromViewData_EvenIfNull() + { + // Arrange + var helper = GetHtmlHelper(); + helper.ViewData["Prefix.StringProperty"] = null; + helper.ViewData.TemplateInfo.HtmlFieldPrefix = "Prefix"; + + // Act + var html = helper.Value("StringProperty", format: null); + + // Assert + Assert.Empty(html); + } + // ValueFor [Fact] @@ -39,7 +141,7 @@ public void ValueForGetsExpressionValueFromViewDataModel() var helper = GetHtmlHelper(); // Act - var html = helper.ValueFor(m => m.StringProperty); + var html = helper.ValueFor(m => m.StringProperty, format: null); // Assert Assert.Equal("ModelStringPropertyValue", html); @@ -76,9 +178,9 @@ public void ValueHelpersWithErrorsGetValueFromModelState() viewData.ModelState["FieldPrefix"] = modelState; // Act & Assert - Assert.Equal("StringPropertyRawValue", helper.Value("StringProperty")); - Assert.Equal("StringPropertyRawValue", helper.ValueFor(m => m.StringProperty)); - Assert.Equal("ModelRawValue", helper.ValueForModel()); + Assert.Equal("StringPropertyRawValue", helper.Value("StringProperty", format: null)); + Assert.Equal("StringPropertyRawValue", helper.ValueFor(m => m.StringProperty, format: null)); + Assert.Equal("ModelRawValue", helper.ValueForModel(format: null)); } [Fact] @@ -91,10 +193,10 @@ public void ValueHelpersWithEmptyNameConvertModelValueUsingCurrentCulture() "{ StringProperty = ModelStringPropertyValue, ObjectProperty = 01/01/1900 00:00:00 }"; // Act & Assert - Assert.Equal(expectedModelValue, helper.Value(expression: string.Empty)); - Assert.Equal(expectedModelValue, helper.Value(expression: null)); // null is another alias for current model - Assert.Equal(expectedModelValue, helper.ValueFor(m => m)); - Assert.Equal(expectedModelValue, helper.ValueForModel()); + Assert.Equal(expectedModelValue, helper.Value(expression: string.Empty, format: null)); + Assert.Equal(expectedModelValue, helper.Value(expression: null, format: null)); // null is another alias for current model + Assert.Equal(expectedModelValue, helper.ValueFor(m => m, format: null)); + Assert.Equal(expectedModelValue, helper.ValueForModel(format: null)); } [Fact] @@ -135,7 +237,7 @@ public void ValueHelpersDoNotEncodeValue() helper.ValueForModel("<{0}>")); Assert.Equal(">", helper.Value("StringProperty", "<{0}>")); Assert.Equal(">", helper.ValueFor(m => m.StringProperty, "<{0}>")); - Assert.Equal("ObjectPropertyRawValue <\"\">", helper.ValueFor(m => m.ObjectProperty)); + Assert.Equal("ObjectPropertyRawValue <\"\">", helper.ValueFor(m => m.ObjectProperty, format: null)); } private sealed class TestModel diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.EmployeeList.html b/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.EmployeeList.html index 50aa85c866..77b6b7d195 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.EmployeeList.html +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.EmployeeList.html @@ -3,13 +3,15 @@
- + +
+ +Name value that should not be seen.
@@ -18,7 +20,10 @@
- + +
@@ -35,13 +40,15 @@
- + +
+ +Name value that should not be seen.
@@ -50,7 +57,10 @@
- + +
@@ -67,13 +77,15 @@
- + +
+ +Name value that should not be seen.
@@ -82,7 +94,10 @@
- + +
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.OrderUsingHtmlHelpers.Encoded.html b/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.OrderUsingHtmlHelpers.Encoded.html index c6e007ac18..b561698901 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.OrderUsingHtmlHelpers.Encoded.html +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.OrderUsingHtmlHelpers.Encoded.html @@ -1,4 +1,4 @@ - + @@ -69,7 +69,8 @@
Male - Female + + Female
  • diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.OrderUsingHtmlHelpers.html b/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.OrderUsingHtmlHelpers.html index 3696a39609..db67181b13 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.OrderUsingHtmlHelpers.html +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.OrderUsingHtmlHelpers.html @@ -1,4 +1,4 @@ - + @@ -69,7 +69,8 @@
    Male - Female + + Female
    • diff --git a/test/WebSites/HtmlGenerationWebSite/Controllers/HtmlGeneration_HomeController.cs b/test/WebSites/HtmlGenerationWebSite/Controllers/HtmlGeneration_HomeController.cs index 4136394f7b..d753cb0443 100644 --- a/test/WebSites/HtmlGenerationWebSite/Controllers/HtmlGeneration_HomeController.cs +++ b/test/WebSites/HtmlGenerationWebSite/Controllers/HtmlGeneration_HomeController.cs @@ -56,14 +56,14 @@ public HtmlGeneration_HomeController() public IActionResult Order() { - ViewBag.Items = _productsListWithSelection; + ViewData["Items"] = _productsListWithSelection; return View(_order); } public IActionResult OrderUsingHtmlHelpers() { - ViewBag.Items = _productsListWithSelection; + ViewData["Items"] = _productsListWithSelection; return View(_order); } @@ -124,12 +124,17 @@ public IActionResult EmployeeList() }, }; + // Extra data that should be ignored within a template. But #1487 currently affects RadioButton and + // TextArea as well as ModelMetadata for
    - @Html.TextAreaFor(m => m.Name) + @* Due to #1487, text area will contain incorrect "Value that should not be seen." *@ + @Html.TextArea(nameof(Model.Name))
    @@ -20,11 +22,14 @@ var genders = new SelectList(new string[] { "Male", "Female" }); } + @* Due to #1487, radio button will not be checked. Employee is Female but incorrect information does not match. *@ + @Html.RadioButton(nameof(Model.Gender), "Female", htmlAttributes: new { disabled = "disabled", @readonly = "readonly" }) + @* Due to #1487,
    - @Html.CheckBoxFor(m=>m.Remote) + @Html.CheckBoxFor(m => m.Remote)
    diff --git a/test/WebSites/HtmlGenerationWebSite/Views/HtmlGeneration_Home/EditorTemplates/GenderUsingHtmlHelpers.cshtml b/test/WebSites/HtmlGenerationWebSite/Views/HtmlGeneration_Home/EditorTemplates/GenderUsingHtmlHelpers.cshtml index 0522a5af33..8317d51140 100644 --- a/test/WebSites/HtmlGenerationWebSite/Views/HtmlGeneration_Home/EditorTemplates/GenderUsingHtmlHelpers.cshtml +++ b/test/WebSites/HtmlGenerationWebSite/Views/HtmlGeneration_Home/EditorTemplates/GenderUsingHtmlHelpers.cshtml @@ -1,4 +1,5 @@ @using HtmlGenerationWebSite.Models @model Gender @Html.RadioButtonFor(m => m, value: "Male") Male -@Html.RadioButtonFor(m => m, value: "Female") Female \ No newline at end of file +@* Due to #2622 radio button will not be checked because help ignores this expression." *@ +@Html.RadioButton(expression: string.Empty, value: "Female") Female \ No newline at end of file