diff --git a/src/Mvc/Mvc.TagHelpers/src/InputTagHelper.cs b/src/Mvc/Mvc.TagHelpers/src/InputTagHelper.cs index d3df4d963d48..3ce7506e0895 100644 --- a/src/Mvc/Mvc.TagHelpers/src/InputTagHelper.cs +++ b/src/Mvc/Mvc.TagHelpers/src/InputTagHelper.cs @@ -309,29 +309,32 @@ private TagBuilder GenerateCheckBox( "checkbox")); } - // hiddenForCheckboxTag always rendered after the returned element - var hiddenForCheckboxTag = Generator.GenerateHiddenForCheckbox(ViewContext, modelExplorer, For.Name); - if (hiddenForCheckboxTag != null) - { - var renderingMode = - output.TagMode == TagMode.SelfClosing ? TagRenderMode.SelfClosing : TagRenderMode.StartTag; - hiddenForCheckboxTag.TagRenderMode = renderingMode; - if (!hiddenForCheckboxTag.Attributes.ContainsKey("name") && - !string.IsNullOrEmpty(Name)) + if (ViewContext.CheckBoxHiddenInputRenderMode != CheckBoxHiddenInputRenderMode.None) + { + // hiddenForCheckboxTag always rendered after the returned element + var hiddenForCheckboxTag = Generator.GenerateHiddenForCheckbox(ViewContext, modelExplorer, For.Name); + if (hiddenForCheckboxTag != null) { - // The checkbox and hidden elements should have the same name attribute value. Attributes will - // match if both are present because both have a generated value. Reach here in the special case - // where user provided a non-empty fallback name. - hiddenForCheckboxTag.MergeAttribute("name", Name); - } + var renderingMode = + output.TagMode == TagMode.SelfClosing ? TagRenderMode.SelfClosing : TagRenderMode.StartTag; + hiddenForCheckboxTag.TagRenderMode = renderingMode; + if (!hiddenForCheckboxTag.Attributes.ContainsKey("name") && + !string.IsNullOrEmpty(Name)) + { + // The checkbox and hidden elements should have the same name attribute value. Attributes will + // match if both are present because both have a generated value. Reach here in the special case + // where user provided a non-empty fallback name. + hiddenForCheckboxTag.MergeAttribute("name", Name); + } - if (ViewContext.FormContext.CanRenderAtEndOfForm) - { - ViewContext.FormContext.EndOfFormContent.Add(hiddenForCheckboxTag); - } - else - { - output.PostElement.AppendHtml(hiddenForCheckboxTag); + if (ViewContext.CheckBoxHiddenInputRenderMode == CheckBoxHiddenInputRenderMode.EndOfForm && ViewContext.FormContext.CanRenderAtEndOfForm) + { + ViewContext.FormContext.EndOfFormContent.Add(hiddenForCheckboxTag); + } + else + { + output.PostElement.AppendHtml(hiddenForCheckboxTag); + } } } diff --git a/src/Mvc/Mvc.TagHelpers/test/InputTagHelperTest.cs b/src/Mvc/Mvc.TagHelpers/test/InputTagHelperTest.cs index 265ecde1f0d2..557454892b7c 100644 --- a/src/Mvc/Mvc.TagHelpers/test/InputTagHelperTest.cs +++ b/src/Mvc/Mvc.TagHelpers/test/InputTagHelperTest.cs @@ -838,6 +838,243 @@ public void Process_GeneratesFormattedOutput_ForDateTime(string specifiedType, s Assert.Equal(expectedTagName, output.TagName); } + [Fact] + public async Task ProcessAsync_GenerateCheckBox_WithHiddenInputRenderModeNone() + { + var propertyName = "-expression-"; + var expectedTagName = "input"; + var inputTypeName = "checkbox"; + var expectedAttributes = new TagHelperAttributeList + { + { "name", propertyName }, + { "type", inputTypeName }, + { "value", "true" }, + }; + + var metadataProvider = new EmptyModelMetadataProvider(); + var htmlGenerator = new TestableHtmlGenerator(metadataProvider); + var model = false; + var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(bool), model); + var modelExpression = new ModelExpression(name: string.Empty, modelExplorer: modelExplorer); + var viewContext = TestableHtmlGenerator.GetViewContext(model, htmlGenerator, metadataProvider); + + viewContext.CheckBoxHiddenInputRenderMode = CheckBoxHiddenInputRenderMode.None; + + var tagHelper = new InputTagHelper(htmlGenerator) + { + For = modelExpression, + InputTypeName = inputTypeName, + Name = propertyName, + ViewContext = viewContext, + }; + + var attributes = new TagHelperAttributeList + { + { "name", propertyName }, + { "type", inputTypeName }, + }; + + var context = new TagHelperContext(attributes, new Dictionary(), "test"); + var output = new TagHelperOutput( + expectedTagName, + new TagHelperAttributeList(), + getChildContentAsync: (useCachedResult, encoder) => Task.FromResult(result: null)) + { + TagMode = TagMode.SelfClosing, + }; + + // Act + await tagHelper.ProcessAsync(context, output); + + // Assert + Assert.Equal(expectedAttributes, output.Attributes); + Assert.False(output.IsContentModified); + Assert.Equal(expectedTagName, output.TagName); + + Assert.False(viewContext.FormContext.HasEndOfFormContent); + Assert.True(string.IsNullOrEmpty(HtmlContentUtilities.HtmlContentToString(output.PostElement))); + } + + [Fact] + public async Task ProcessAsync_GenerateCheckBox_WithHiddenInputRenderModeInline() + { + var propertyName = "-expression-"; + var expectedTagName = "input"; + var expectedPostElementContent = $""; + var inputTypeName = "checkbox"; + var expectedAttributes = new TagHelperAttributeList + { + { "name", propertyName }, + { "type", inputTypeName }, + { "value", "true" }, + }; + + var metadataProvider = new EmptyModelMetadataProvider(); + var htmlGenerator = new TestableHtmlGenerator(metadataProvider); + var model = false; + var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(bool), model); + var modelExpression = new ModelExpression(name: string.Empty, modelExplorer: modelExplorer); + var viewContext = TestableHtmlGenerator.GetViewContext(model, htmlGenerator, metadataProvider); + + viewContext.FormContext.CanRenderAtEndOfForm = true; + viewContext.CheckBoxHiddenInputRenderMode = CheckBoxHiddenInputRenderMode.Inline; + + var tagHelper = new InputTagHelper(htmlGenerator) + { + For = modelExpression, + InputTypeName = inputTypeName, + Name = propertyName, + ViewContext = viewContext, + }; + + var attributes = new TagHelperAttributeList + { + { "name", propertyName }, + { "type", inputTypeName }, + }; + + var context = new TagHelperContext(attributes, new Dictionary(), "test"); + var output = new TagHelperOutput( + expectedTagName, + new TagHelperAttributeList(), + getChildContentAsync: (useCachedResult, encoder) => Task.FromResult(result: null)) + { + TagMode = TagMode.SelfClosing, + }; + + // Act + await tagHelper.ProcessAsync(context, output); + + // Assert + Assert.Equal(expectedAttributes, output.Attributes); + Assert.False(output.IsContentModified); + Assert.Equal(expectedTagName, output.TagName); + + Assert.False(viewContext.FormContext.HasEndOfFormContent); + Assert.Equal(expectedPostElementContent, HtmlContentUtilities.HtmlContentToString(output.PostElement)); + } + + [Fact] + public async Task ProcessAsync_GenerateCheckBox_WithHiddenInputRenderModeEndOfForm() + { + var propertyName = "-expression-"; + var expectedTagName = "input"; + var expectedEndOfFormContent = $""; + var inputTypeName = "checkbox"; + var expectedAttributes = new TagHelperAttributeList + { + { "name", propertyName }, + { "type", inputTypeName }, + { "value", "true" }, + }; + + var metadataProvider = new EmptyModelMetadataProvider(); + var htmlGenerator = new TestableHtmlGenerator(metadataProvider); + var model = false; + var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(bool), model); + var modelExpression = new ModelExpression(name: string.Empty, modelExplorer: modelExplorer); + var viewContext = TestableHtmlGenerator.GetViewContext(model, htmlGenerator, metadataProvider); + + viewContext.FormContext.CanRenderAtEndOfForm = true; + viewContext.CheckBoxHiddenInputRenderMode = CheckBoxHiddenInputRenderMode.EndOfForm; + + var tagHelper = new InputTagHelper(htmlGenerator) + { + For = modelExpression, + InputTypeName = inputTypeName, + Name = propertyName, + ViewContext = viewContext, + }; + + var attributes = new TagHelperAttributeList + { + { "name", propertyName }, + { "type", inputTypeName }, + }; + + var context = new TagHelperContext(attributes, new Dictionary(), "test"); + var output = new TagHelperOutput( + expectedTagName, + new TagHelperAttributeList(), + getChildContentAsync: (useCachedResult, encoder) => Task.FromResult(result: null)) + { + TagMode = TagMode.SelfClosing, + }; + + // Act + await tagHelper.ProcessAsync(context, output); + + // Assert + Assert.Equal(expectedAttributes, output.Attributes); + Assert.False(output.IsContentModified); + Assert.Equal(expectedTagName, output.TagName); + + Assert.Equal(expectedEndOfFormContent, string.Join("", viewContext.FormContext.EndOfFormContent.Select(html => HtmlContentUtilities.HtmlContentToString(html)))); + Assert.True(string.IsNullOrEmpty(HtmlContentUtilities.HtmlContentToString(output.PostElement))); + } + + [Fact] + public async Task ProcessAsync_GenerateCheckBox_WithHiddenInputRenderModeEndOfForm_AndCanRenderAtEndOfFormNotSet() + { + var propertyName = "-expression-"; + var expectedTagName = "input"; + var expectedPostElementContent = $""; + var inputTypeName = "checkbox"; + var expectedAttributes = new TagHelperAttributeList + { + { "name", propertyName }, + { "type", inputTypeName }, + { "value", "true" }, + }; + + var metadataProvider = new EmptyModelMetadataProvider(); + var htmlGenerator = new TestableHtmlGenerator(metadataProvider); + var model = false; + var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(bool), model); + var modelExpression = new ModelExpression(name: string.Empty, modelExplorer: modelExplorer); + var viewContext = TestableHtmlGenerator.GetViewContext(model, htmlGenerator, metadataProvider); + + viewContext.FormContext.CanRenderAtEndOfForm = false; + viewContext.CheckBoxHiddenInputRenderMode = CheckBoxHiddenInputRenderMode.EndOfForm; + + var tagHelper = new InputTagHelper(htmlGenerator) + { + For = modelExpression, + InputTypeName = inputTypeName, + Name = propertyName, + ViewContext = viewContext, + }; + + var attributes = new TagHelperAttributeList + { + { "name", propertyName }, + { "type", inputTypeName }, + }; + + var context = new TagHelperContext(attributes, new Dictionary(), "test"); + var output = new TagHelperOutput( + expectedTagName, + new TagHelperAttributeList(), + getChildContentAsync: (useCachedResult, encoder) => Task.FromResult(result: null)) + { + TagMode = TagMode.SelfClosing, + }; + + // Act + await tagHelper.ProcessAsync(context, output); + + // Assert + Assert.Equal(expectedAttributes, output.Attributes); + Assert.False(output.IsContentModified); + Assert.Equal(expectedTagName, output.TagName); + + Assert.False(viewContext.FormContext.HasEndOfFormContent); + Assert.Equal(expectedPostElementContent, HtmlContentUtilities.HtmlContentToString(output.PostElement)); + } + [Fact] public async Task ProcessAsync_CallsGenerateCheckBox_WithExpectedParameters() { diff --git a/src/Mvc/Mvc.ViewFeatures/ref/Microsoft.AspNetCore.Mvc.ViewFeatures.netcoreapp3.0.cs b/src/Mvc/Mvc.ViewFeatures/ref/Microsoft.AspNetCore.Mvc.ViewFeatures.netcoreapp3.0.cs index 1c78011d63ea..9125dacce40f 100644 --- a/src/Mvc/Mvc.ViewFeatures/ref/Microsoft.AspNetCore.Mvc.ViewFeatures.netcoreapp3.0.cs +++ b/src/Mvc/Mvc.ViewFeatures/ref/Microsoft.AspNetCore.Mvc.ViewFeatures.netcoreapp3.0.cs @@ -312,6 +312,12 @@ public static void TryAddModelException(this Microsoft.AspNetCore.Mvc.Mo } namespace Microsoft.AspNetCore.Mvc.Rendering { + public enum CheckBoxHiddenInputRenderMode + { + None = 0, + Inline = 1, + EndOfForm = 2, + } public enum FormMethod { Get = 0, @@ -678,6 +684,7 @@ public partial class ViewContext : Microsoft.AspNetCore.Mvc.ActionContext public ViewContext() { } public ViewContext(Microsoft.AspNetCore.Mvc.ActionContext actionContext, Microsoft.AspNetCore.Mvc.ViewEngines.IView view, Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary viewData, Microsoft.AspNetCore.Mvc.ViewFeatures.ITempDataDictionary tempData, System.IO.TextWriter writer, Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlHelperOptions htmlHelperOptions) { } public ViewContext(Microsoft.AspNetCore.Mvc.Rendering.ViewContext viewContext, Microsoft.AspNetCore.Mvc.ViewEngines.IView view, Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary viewData, System.IO.TextWriter writer) { } + public Microsoft.AspNetCore.Mvc.Rendering.CheckBoxHiddenInputRenderMode CheckBoxHiddenInputRenderMode { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public bool ClientValidationEnabled { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public string ExecutingFilePath { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public virtual Microsoft.AspNetCore.Mvc.ViewFeatures.FormContext FormContext { get { throw null; } set { } } @@ -1062,6 +1069,7 @@ public void EndForm() { } public partial class HtmlHelperOptions { public HtmlHelperOptions() { } + public Microsoft.AspNetCore.Mvc.Rendering.CheckBoxHiddenInputRenderMode CheckBoxHiddenInputRenderMode { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public bool ClientValidationEnabled { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public Microsoft.AspNetCore.Mvc.Rendering.Html5DateRenderingMode Html5DateRenderingMode { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public string IdAttributeDotReplacement { get { throw null; } set { } } diff --git a/src/Mvc/Mvc.ViewFeatures/src/HtmlHelper.cs b/src/Mvc/Mvc.ViewFeatures/src/HtmlHelper.cs index 016246f4a407..cf8ee07fd90b 100644 --- a/src/Mvc/Mvc.ViewFeatures/src/HtmlHelper.cs +++ b/src/Mvc/Mvc.ViewFeatures/src/HtmlHelper.cs @@ -721,8 +721,18 @@ protected virtual IHtmlContent GenerateCheckBox( isChecked, htmlAttributes); + if (checkbox == null) + { + return HtmlString.Empty; + } + + if (ViewContext.CheckBoxHiddenInputRenderMode == CheckBoxHiddenInputRenderMode.None) + { + return checkbox; + } + var hiddenForCheckbox = _htmlGenerator.GenerateHiddenForCheckbox(ViewContext, modelExplorer, expression); - if (checkbox == null || hiddenForCheckbox == null) + if (hiddenForCheckbox == null) { return HtmlString.Empty; } @@ -736,7 +746,7 @@ protected virtual IHtmlContent GenerateCheckBox( hiddenForCheckbox.MergeAttribute("name", name); } - if (ViewContext.FormContext.CanRenderAtEndOfForm) + if (ViewContext.CheckBoxHiddenInputRenderMode == CheckBoxHiddenInputRenderMode.EndOfForm && ViewContext.FormContext.CanRenderAtEndOfForm) { ViewContext.FormContext.EndOfFormContent.Add(hiddenForCheckbox); return checkbox; diff --git a/src/Mvc/Mvc.ViewFeatures/src/HtmlHelperOptions.cs b/src/Mvc/Mvc.ViewFeatures/src/HtmlHelperOptions.cs index 952bc7b4d9a7..f40bdf93f697 100644 --- a/src/Mvc/Mvc.ViewFeatures/src/HtmlHelperOptions.cs +++ b/src/Mvc/Mvc.ViewFeatures/src/HtmlHelperOptions.cs @@ -56,5 +56,10 @@ public string IdAttributeDotReplacement /// and other overloads. /// public string ValidationSummaryMessageElement { get; set; } = "span"; + + /// + /// Gets or sets the way hidden inputs are rendered for checkbox tag helpers and html helpers. + /// + public CheckBoxHiddenInputRenderMode CheckBoxHiddenInputRenderMode { get; set; } = CheckBoxHiddenInputRenderMode.EndOfForm; } } diff --git a/src/Mvc/Mvc.ViewFeatures/src/Rendering/CheckBoxHiddenInputRenderMode.cs b/src/Mvc/Mvc.ViewFeatures/src/Rendering/CheckBoxHiddenInputRenderMode.cs new file mode 100644 index 000000000000..3d89c44d8028 --- /dev/null +++ b/src/Mvc/Mvc.ViewFeatures/src/Rendering/CheckBoxHiddenInputRenderMode.cs @@ -0,0 +1,27 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Mvc.Rendering +{ + /// + /// Controls the rendering of hidden input fields when using CheckBox tag helpers or html helpers. + /// + public enum CheckBoxHiddenInputRenderMode + { + /// + /// Hidden input fields will not be automatically rendered. If checkbox is not checked, no value will be posted. + /// + None = 0, + + /// + /// Hidden input fields will be rendered inline with each checkbox. Use this for legacy ASP.NET MVC behavior. + /// + Inline = 1, + + /// + /// Hidden input fields will be rendered for each checkbox at the bottom of the form element. This is the preferred render method and default MVC behavior. + /// If is false, will fall back on . + /// + EndOfForm = 2 + } +} diff --git a/src/Mvc/Mvc.ViewFeatures/src/Rendering/ViewContext.cs b/src/Mvc/Mvc.ViewFeatures/src/Rendering/ViewContext.cs index f80bf61758ec..b5069c7ca6d0 100644 --- a/src/Mvc/Mvc.ViewFeatures/src/Rendering/ViewContext.cs +++ b/src/Mvc/Mvc.ViewFeatures/src/Rendering/ViewContext.cs @@ -87,6 +87,7 @@ public ViewContext( Html5DateRenderingMode = htmlHelperOptions.Html5DateRenderingMode; ValidationSummaryMessageElement = htmlHelperOptions.ValidationSummaryMessageElement; ValidationMessageElement = htmlHelperOptions.ValidationMessageElement; + CheckBoxHiddenInputRenderMode = htmlHelperOptions.CheckBoxHiddenInputRenderMode; } /// @@ -129,6 +130,7 @@ public ViewContext( Html5DateRenderingMode = viewContext.Html5DateRenderingMode; ValidationSummaryMessageElement = viewContext.ValidationSummaryMessageElement; ValidationMessageElement = viewContext.ValidationMessageElement; + CheckBoxHiddenInputRenderMode = viewContext.CheckBoxHiddenInputRenderMode; ExecutingFilePath = viewContext.ExecutingFilePath; View = view; @@ -180,6 +182,11 @@ public virtual FormContext FormContext /// public string ValidationMessageElement { get; set; } + /// + /// Gets or sets the way hidden inputs are rendered for checkbox tag helpers and html helpers. + /// + public CheckBoxHiddenInputRenderMode CheckBoxHiddenInputRenderMode { get; set; } + /// /// Gets the dynamic view bag. /// diff --git a/src/Mvc/Mvc.ViewFeatures/test/Rendering/HtmlHelperCheckboxTest.cs b/src/Mvc/Mvc.ViewFeatures/test/Rendering/HtmlHelperCheckboxTest.cs index 67cfb7f64e7b..ab357622556a 100644 --- a/src/Mvc/Mvc.ViewFeatures/test/Rendering/HtmlHelperCheckboxTest.cs +++ b/src/Mvc/Mvc.ViewFeatures/test/Rendering/HtmlHelperCheckboxTest.cs @@ -169,6 +169,93 @@ public void CheckBox_WithCanRenderAtEndOfFormSet_DoesNotGenerateInlineHiddenTag( writer.ToString()); } + [Fact] + public void CheckBox_WithHiddenInputRenderModeNone_DoesNotGenerateHiddenInput() + { + // Arrange + var requiredMessage = ValidationAttributeUtil.GetRequiredErrorMessage("Boolean"); + var expected = @""; + var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetTestModelViewData()); + helper.ViewContext.CheckBoxHiddenInputRenderMode = CheckBoxHiddenInputRenderMode.None; + + // Act + var html = helper.CheckBox("Property1", isChecked: true, htmlAttributes: null); + + // Assert + Assert.False(helper.ViewContext.FormContext.HasEndOfFormContent); + Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(html)); + } + + [Fact] + public void CheckBox_WithHiddenInputRenderModeInline_GeneratesHiddenInput() + { + // Arrange + var requiredMessage = ValidationAttributeUtil.GetRequiredErrorMessage("Boolean"); + var expected = @""; + var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetTestModelViewData()); + helper.ViewContext.FormContext.CanRenderAtEndOfForm = true; + helper.ViewContext.CheckBoxHiddenInputRenderMode = CheckBoxHiddenInputRenderMode.Inline; + + // Act + var html = helper.CheckBox("Property1", isChecked: true, htmlAttributes: null); + + // Assert + Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(html)); + } + + [Fact] + public void CheckBox_WithHiddenInputRenderModeEndOfForm_GeneratesHiddenInput() + { + // Arrange + var requiredMessage = ValidationAttributeUtil.GetRequiredErrorMessage("Boolean"); + var expected = @""; + var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetTestModelViewData()); + helper.ViewContext.FormContext.CanRenderAtEndOfForm = true; + helper.ViewContext.CheckBoxHiddenInputRenderMode = CheckBoxHiddenInputRenderMode.EndOfForm; + + // Act + var html = helper.CheckBox("Property1", isChecked: true, htmlAttributes: null); + + // Assert + Assert.True(helper.ViewContext.FormContext.HasEndOfFormContent); + Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(html)); + var writer = new StringWriter(); + var hiddenTag = Assert.Single(helper.ViewContext.FormContext.EndOfFormContent); + hiddenTag.WriteTo(writer, new HtmlTestEncoder()); + Assert.Equal("", + writer.ToString()); + } + + [Fact] + public void CheckBox_WithHiddenInputRenderModeEndOfForm_WithCanRenderAtEndOfFormNotSet_GeneratesHiddenInput() + { + // Arrange + var requiredMessage = ValidationAttributeUtil.GetRequiredErrorMessage("Boolean"); + var expected = @""; + var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetTestModelViewData()); + helper.ViewContext.FormContext.CanRenderAtEndOfForm = false; + helper.ViewContext.CheckBoxHiddenInputRenderMode = CheckBoxHiddenInputRenderMode.EndOfForm; + + // Act + var html = helper.CheckBox("Property1", isChecked: true, htmlAttributes: null); + + // Assert + Assert.False(helper.ViewContext.FormContext.HasEndOfFormContent); + Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(html)); + } + [Fact] public void CheckBoxUsesAttemptedValueFromModelState() {