// 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. using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq.Expressions; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.TestCommon; using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Testing; using Xunit; namespace Microsoft.AspNetCore.Mvc.Rendering { public class HtmlHelperPasswordTest { public static IEnumerable PasswordWithViewDataAndAttributesData { get { var attributes1 = new Dictionary { { "test-key", "test-value" }, { "value", "attribute-value" } }; var attributes2 = new { test_key = "test-value", value = "attribute-value" }; var vdd = GetViewDataWithModelStateAndModelAndViewDataValues(); vdd.Model.Property1 = "does-not-get-used"; yield return new object[] { vdd, attributes1 }; yield return new object[] { vdd, attributes2 }; var nullModelVdd = GetViewDataWithNullModelAndNonEmptyViewData(); yield return new object[] { nullModelVdd, attributes1 }; yield return new object[] { nullModelVdd, attributes2 }; } } [Theory] [MemberData(nameof(PasswordWithViewDataAndAttributesData))] public void Password_UsesAttributeValueWhenValueArgumentIsNull( ViewDataDictionary vdd, object attributes) { // Arrange var expected = @""; var helper = DefaultTemplatesUtilities.GetHtmlHelper(vdd); // Act var result = helper.Password("Property1", value: null, htmlAttributes: attributes); // Assert Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(result)); } [Theory] [MemberData(nameof(PasswordWithViewDataAndAttributesData))] public void Password_UsesExplicitValue_IfSpecified( ViewDataDictionary vdd, object attributes) { // Arrange var expected = @""; var helper = DefaultTemplatesUtilities.GetHtmlHelper(vdd); // Act var result = helper.Password("Property1", "explicit-value", attributes); // Assert Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(result)); } [Fact] public void PasswordWithPrefix_GeneratesExpectedValue() { // Arrange var expected = @""; var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetViewDataWithModelStateAndModelAndViewDataValues()); helper.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix = "MyPrefix"; // Act var result = helper.Password("Property1", "explicit-value", htmlAttributes: null); // Assert Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(result)); } [Fact] public void PasswordWithPrefix_UsesIdDotReplacementToken() { // Arrange var expected = @""; var helper = DefaultTemplatesUtilities.GetHtmlHelper( GetViewDataWithModelStateAndModelAndViewDataValues(), idAttributeDotReplacement: "$"); helper.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix = "MyPrefix"; // Act var result = helper.Password("Property1", "explicit-value", htmlAttributes: null); // Assert Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(result)); } [Fact] public void PasswordWithPrefixAndEmptyName_GeneratesExpectedValue() { // Arrange var expected = @""; var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetViewDataWithModelStateAndModelAndViewDataValues()); helper.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix = "MyPrefix"; var name = string.Empty; // Act var result = helper.Password(name, "explicit-value", htmlAttributes: null); // Assert Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(result)); } [Fact] public void PasswordWithEmptyNameAndPrefixThrows() { // Arrange var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetViewDataWithModelStateAndModelAndViewDataValues()); var name = string.Empty; var value = string.Empty; var expectedMessage = "The name of an HTML field cannot be null or empty. Instead use methods " + "Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper.Editor or Microsoft.AspNetCore.Mvc.Rendering." + "IHtmlHelper`1.EditorFor with a non-empty htmlFieldName argument value."; // Act and Assert ExceptionAssert.ThrowsArgument( () => helper.Password(name, value, htmlAttributes: null), "expression", expectedMessage); } [Fact] public void Password_UsesModelStateErrors_ButDoesNotUseModelOrViewDataOrModelStateForValueAttribute() { // Arrange var expected = @""; var vdd = GetViewDataWithErrors(); vdd.Model.Property1 = "property-value"; var helper = DefaultTemplatesUtilities.GetHtmlHelper(vdd); var attributes = new Dictionary { { "test-key", "test-value" }, { "class", "some-class"} }; // Act var result = helper.Password("Property1", value: null, htmlAttributes: attributes); // Assert Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(result)); } [Fact] public void PasswordGeneratesUnobtrusiveValidation() { // Arrange var requiredMessage = ValidationAttributeUtil.GetRequiredErrorMessage("Property2"); var expected = $@""; var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetViewDataWithModelStateAndModelAndViewDataValues()); // Act var result = helper.Password("Property2", value: null, htmlAttributes: null); // Assert Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(result)); } public static IEnumerable PasswordWithComplexExpressions_UsesIdDotSeparatorData { get { yield return new object[] { "Property4.Property5", @"", }; yield return new object[] { "Property4.Property6[0]", @"", }; } } [Theory] [MemberData(nameof(PasswordWithComplexExpressions_UsesIdDotSeparatorData))] public void PasswordWithComplexExpressions_UsesIdDotSeparator(string expression, string expected) { // Arrange var viewData = GetViewDataWithModelStateAndModelAndViewDataValues(); var helper = DefaultTemplatesUtilities.GetHtmlHelper(viewData, idAttributeDotReplacement: "$$"); var attributes = new Dictionary { { "data-test", "val" } }; // Act var result = helper.Password(expression, value: null, htmlAttributes: attributes); // Assert Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(result)); } [Theory] [MemberData(nameof(PasswordWithViewDataAndAttributesData))] public void PasswordForWithAttributes_GeneratesExpectedValue( ViewDataDictionary vdd, object htmlAttributes) { // Arrange var expected = @""; var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetViewDataWithModelStateAndModelAndViewDataValues()); helper.ViewData.Model.Property1 = "test"; // Act var result = helper.PasswordFor(m => m.Property1, htmlAttributes); // Assert Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(result)); } [Fact] public void PasswordForWithPrefix_GeneratesExpectedValue() { // Arrange var expected = @""; var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetViewDataWithModelStateAndModelAndViewDataValues()); helper.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix = "MyPrefix"; // Act var result = helper.PasswordFor(m => m.Property1, htmlAttributes: null); // Assert Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(result)); } [Fact] public void PasswordFor_UsesModelStateErrors_ButDoesNotUseModelOrViewDataOrModelStateForValueAttribute() { // Arrange var expected = @""; var vdd = GetViewDataWithErrors(); vdd.Model.Property1 = "prop1-value"; var helper = DefaultTemplatesUtilities.GetHtmlHelper(vdd); var attributes = new Dictionary { { "baz", "BazValue" }, { "class", "some-class"} }; // Act var result = helper.PasswordFor(m => m.Property1, attributes); // Assert Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(result)); } [Fact] public void PasswordFor_GeneratesUnobtrusiveValidationAttributes() { // Arrange var requiredMessage = ValidationAttributeUtil.GetRequiredErrorMessage("Property2"); var expected = $@""; var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetViewDataWithErrors()); // Act var result = helper.PasswordFor(m => m.Property2, htmlAttributes: null); // Assert Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(result)); } public static TheoryData PasswordFor_WithComplexExpressionsData { get { return new TheoryData>, string> { { model => model.Property3["key"], @"" }, { model => model.Property4.Property5, @"" }, { model => model.Property4.Property6[0], @"" } }; } } [Theory] [MemberData(nameof(PasswordFor_WithComplexExpressionsData))] public void PasswordFor_WithComplexExpressionsAndFieldPrefix_UsesAttributeValueIfSpecified( Expression> expression, string expected) { // Arrange var viewData = GetViewDataWithModelStateAndModelAndViewDataValues(); viewData.ModelState.SetModelValue("pre.Property3[key]", "Property3Val", "Property3Val"); viewData.ModelState.SetModelValue("pre.Property4.Property5", "Property5Val", "Property5Val"); viewData.ModelState.SetModelValue("pre.Property4.Property6[0]", "Property6Val", "Property6Val"); viewData["pre.Property3[key]"] = "vdd-value1"; viewData["pre.Property4.Property5"] = "vdd-value2"; viewData["pre.Property4.Property6[0]"] = "vdd-value3"; viewData.Model.Property3["key"] = "prop-value1"; viewData.Model.Property4.Property5 = "prop-value2"; viewData.Model.Property4.Property6.Add("prop-value3"); var helper = DefaultTemplatesUtilities.GetHtmlHelper(viewData); viewData.TemplateInfo.HtmlFieldPrefix = "pre"; var attributes = new { data_val = "true", value = "attr-value" }; // Act var result = helper.PasswordFor(expression, attributes); // Assert Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(result)); } [Fact] public void Password_UsesSpecifiedExpressionForNames_IgnoresExpressionValue() { // Arrange var metadataProvider = new EmptyModelMetadataProvider(); var helper = DefaultTemplatesUtilities.GetHtmlHelper(new ViewDataDictionary(metadataProvider)); helper.ViewContext.ClientValidationEnabled = false; helper.ViewData.Model = new TestModel { Property1 = "propValue" }; // Act var passwordResult = helper.Password("Property1"); // Assert Assert.Equal( "", HtmlContentUtilities.HtmlContentToString(passwordResult)); } [Fact] public void PasswordFor_UsesSpecifiedExpressionForNames_IgnoresExpressionValue() { // Arrange var metadataProvider = new EmptyModelMetadataProvider(); var helper = DefaultTemplatesUtilities.GetHtmlHelper(new ViewDataDictionary(metadataProvider)); helper.ViewContext.ClientValidationEnabled = false; helper.ViewData.Model = new TestModel { Property1 = "propValue" }; // Act var passwordForResult = helper.PasswordFor(m => m.Property1); // Assert Assert.Equal( "", HtmlContentUtilities.HtmlContentToString(passwordForResult)); } [Fact] public void Password_UsesSpecifiedValue() { // Arrange var metadataProvider = new EmptyModelMetadataProvider(); var helper = DefaultTemplatesUtilities.GetHtmlHelper(new ViewDataDictionary(metadataProvider)); helper.ViewContext.ClientValidationEnabled = false; helper.ViewData.Model = new TestModel { Property1 = "propValue" }; // Act var passwordResult = helper.Password("Property1", value: "myvalue"); // Assert Assert.Equal( "", HtmlContentUtilities.HtmlContentToString(passwordResult)); } [Fact] public void PasswordFor_GeneratesPlaceholderAttribute_WhenDisplayAttributePromptIsSet() { // Arrange var expected = @""; var model = new PasswordModel(); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model); // Act var result = helper.PasswordFor(m => m.Property7, htmlAttributes: null); // Assert Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(result)); } private static ViewDataDictionary GetViewDataWithNullModelAndNonEmptyViewData() { return new ViewDataDictionary(new EmptyModelMetadataProvider()) { ["Property1"] = "view-data-val", }; } private static ViewDataDictionary GetViewDataWithModelStateAndModelAndViewDataValues() { var viewData = GetViewDataWithNullModelAndNonEmptyViewData(); viewData.Model = new PasswordModel(); viewData.ModelState.SetModelValue("Property1", "ModelStateValue", "ModelStateValue"); return viewData; } private static ViewDataDictionary GetViewDataWithErrors() { var viewData = GetViewDataWithModelStateAndModelAndViewDataValues(); viewData.ModelState.AddModelError("Property1", "error 1"); viewData.ModelState.AddModelError("Property1", "error 2"); return viewData; } public static TheoryData PasswordFor_IgnoresExpressionValueForComplexExpressionsData { get { return new TheoryData>, string> { { model => model.Property3["key"], @"" }, { model => model.Property4.Property5, @"" }, { model => model.Property4.Property6[0], @"" } }; } } [Theory] [MemberData(nameof(PasswordFor_IgnoresExpressionValueForComplexExpressionsData))] public void PasswordFor_ComplexExpressions_IgnoresValueFromViewDataModelStateAndModel( Expression> expression, string expected) { // Arrange var model = new PasswordModel(); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model); helper.ViewData.TemplateInfo.HtmlFieldPrefix = "pre"; helper.ViewData.ModelState.SetModelValue("pre.Property3[key]", "MProp3Val", "MProp3Val"); helper.ViewData.ModelState.SetModelValue("pre.Property4.Property5", "MProp5Val", "MProp5Val"); helper.ViewData.ModelState.SetModelValue("pre.Property4.Property6[0]", "MProp6Val", "MProp6Val"); helper.ViewData["pre.Property3[key]"] = "VDProp3Val"; helper.ViewData["pre.Property4.Property5"] = "VDProp5Val"; helper.ViewData["pre.Property4.Property6"] = "VDProp6Val"; helper.ViewData.Model.Property3["key"] = "Prop3Val"; helper.ViewData.Model.Property4.Property5 = "Prop5Val"; helper.ViewData.Model.Property4.Property6.Add("Prop6Val"); // Act var result = helper.PasswordFor(expression); // Assert Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(result)); } public class PasswordModel { public string Property1 { get; set; } [Required] public string Property2 { get; set; } public Dictionary Property3 { get; } = new Dictionary(); public NestedClass Property4 { get; } = new NestedClass(); [Display(Prompt = "placeholder")] public string Property7 { get; set; } } public class NestedClass { public string Property5 { get; set; } public List Property6 { get; } = new List(); } private class TestModel { public string Property1 { get; set; } } } }