// 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.ComponentModel.DataAnnotations; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.TestCommon; using Microsoft.AspNetCore.Mvc.ViewEngines; using Microsoft.Extensions.Localization; using Moq; using Xunit; namespace Microsoft.AspNetCore.Mvc.Rendering { public class HtmlHelperDisplayExtensionsTest { [Fact] public void DisplayHelpers_FindsModel_WhenViewDataIsNotSet() { // Arrange var expected = $"
HtmlEncode[[SomeProperty]]
{Environment.NewLine}" + $"
HtmlEncode[[PropValue]]
{Environment.NewLine}"; var model = new SomeModel { SomeProperty = "PropValue" }; var viewEngine = new Mock(MockBehavior.Strict); viewEngine .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny(), /*isMainPage*/ false)) .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty())); viewEngine .Setup(v => v.FindView(It.IsAny(), It.IsAny(), /*isMainPage*/ false)) .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty())); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); // Act var displayResult = helper.Display(expression: string.Empty); var displayNullResult = helper.Display(expression: null); // null is another alias for current model var displayForResult = helper.DisplayFor(m => m); var displayForModelResult = helper.DisplayForModel(); // Assert Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(displayResult)); Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(displayNullResult)); Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(displayForResult)); Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(displayForModelResult)); } [Fact] public void Display_UsesAdditionalViewData() { // Arrange var model = new SomeModel { SomeProperty = "ModelValue" }; var view = new Mock(); view.Setup(v => v.RenderAsync(It.IsAny())) .Callback((ViewContext v) => v.Writer.WriteAsync(v.ViewData["SomeProperty"].ToString())) .Returns(Task.FromResult(0)); var viewEngine = new Mock(MockBehavior.Strict); viewEngine .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny(), /*isMainPage*/ false)) .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty())); viewEngine .Setup(v => v.FindView(It.IsAny(), It.IsAny(), /*isMainPage*/ false)) .Returns(ViewEngineResult.Found("SomeView", view.Object)); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); // Act var displayResult = helper.Display(expression: "SomeProperty", additionalViewData: new { SomeProperty = "ViewDataValue" }); // Assert Assert.Equal("ViewDataValue", HtmlContentUtilities.HtmlContentToString(displayResult)); } [Fact] public void Display_UsesTemplateName() { // Arrange var model = new SomeModel { SomeProperty = "ModelValue" }; var viewEngine = new Mock(MockBehavior.Strict); viewEngine .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny(), /*isMainPage*/ false)) .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty())); viewEngine .Setup(v => v.FindView(It.IsAny(), "DisplayTemplates/SomeTemplate", /*isMainPage*/ false)) .Returns(ViewEngineResult.Found("SomeView", Mock.Of())) .Verifiable(); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); // Act var displayResult = helper.Display(expression: "SomeProperty", templateName: "SomeTemplate"); // Assert viewEngine.Verify(); } public static TheoryData EnumFormatModels { get { return new TheoryData { { new FormatModel{ FormatProperty = Status.Created }, "Value: CreatedKey" }, { new FormatModel { FormatProperty = Status.Done }, "Value: Done" } }; } } public static TheoryData EnumUnformattedModels { get { return new TheoryData { { new FormatModel {NonFormatProperty = Status.Created }, "CreatedKey" }, { new FormatModel {NonFormatProperty = Status.Done }, "Done" } }; } } [Theory] [MemberData(nameof(EnumUnformattedModels))] public void Display_UsesTemplateUnFormatted(FormatModel model, string expectedResult) { // Arrange var view = new Mock(); view.Setup(v => v.RenderAsync(It.IsAny())) .Callback((ViewContext v) => v.Writer.WriteAsync(v.ViewData.TemplateInfo.FormattedModelValue.ToString())) .Returns(Task.FromResult(0)); var viewEngine = new Mock(MockBehavior.Strict); viewEngine .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny(), /*isMainPage*/ false)) .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty())); viewEngine .Setup(v => v.FindView(It.IsAny(), "DisplayTemplates/Status", /*isMainPage*/ false)) .Returns(ViewEngineResult.Found("Status", view.Object)) .Verifiable(); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); // Act var displayResult = helper.DisplayFor(x => x.NonFormatProperty); // Assert Assert.Equal(expectedResult, HtmlContentUtilities.HtmlContentToString(displayResult)); } [Theory] [MemberData(nameof(EnumFormatModels))] public void Display_UsesTemplateFormatted(FormatModel model, string expectedResult) { // Arrange var view = new Mock(); view.Setup(v => v.RenderAsync(It.IsAny())) .Callback((ViewContext v) => v.Writer.WriteAsync(v.ViewData.TemplateInfo.FormattedModelValue.ToString())) .Returns(Task.FromResult(0)); var viewEngine = new Mock(MockBehavior.Strict); viewEngine .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny(), /*isMainPage*/ false)) .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty())); viewEngine .Setup(v => v.FindView(It.IsAny(), "DisplayTemplates/Status", /*isMainPage*/ false)) .Returns(ViewEngineResult.Found("Status", view.Object)) .Verifiable(); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); // Act var displayResult = helper.DisplayFor(x => x.FormatProperty); // Assert Assert.Equal(expectedResult, HtmlContentUtilities.HtmlContentToString(displayResult)); } [Fact] public void Display_UsesTemplateNameAndAdditionalViewData() { // Arrange var model = new SomeModel { SomeProperty = "ModelValue" }; var view = new Mock(); view.Setup(v => v.RenderAsync(It.IsAny())) .Callback((ViewContext v) => v.Writer.WriteAsync(v.ViewData["SomeProperty"].ToString())) .Returns(Task.FromResult(0)); var viewEngine = new Mock(MockBehavior.Strict); viewEngine .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny(), /*isMainPage*/ false)) .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty())); viewEngine .Setup(v => v.FindView(It.IsAny(), "DisplayTemplates/SomeTemplate", /*isMainPage*/ false)) .Returns(ViewEngineResult.Found("SomeView", view.Object)); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); // Act var displayResult = helper.Display( expression: "SomeProperty", templateName: "SomeTemplate", additionalViewData: new { SomeProperty = "ViewDataValue" }); // Assert Assert.Equal("ViewDataValue", HtmlContentUtilities.HtmlContentToString(displayResult)); } [Fact] public void Display_UsesTemplateNameAndHtmlFieldName() { // Arrange var model = new SomeModel { SomeProperty = "ModelValue" }; var view = new Mock(); view.Setup(v => v.RenderAsync(It.IsAny())) .Callback((ViewContext v) => v.Writer.WriteAsync(v.ViewData.TemplateInfo.HtmlFieldPrefix)) .Returns(Task.FromResult(0)); var viewEngine = new Mock(MockBehavior.Strict); viewEngine .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny(), /*isMainPage*/ false)) .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty())); viewEngine .Setup(v => v.FindView(It.IsAny(), "DisplayTemplates/SomeTemplate", /*isMainPage*/ false)) .Returns(ViewEngineResult.Found("SomeView", view.Object)); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); // Act var displayResult = helper.Display( expression: "SomeProperty", templateName: "SomeTemplate", htmlFieldName: "SomeField"); // Assert Assert.Equal("SomeField", HtmlContentUtilities.HtmlContentToString(displayResult)); } [Fact] public void DisplayFor_UsesAdditionalViewData() { // Arrange var model = new SomeModel { SomeProperty = "ModelValue" }; var view = new Mock(); view.Setup(v => v.RenderAsync(It.IsAny())) .Callback((ViewContext v) => v.Writer.WriteAsync(v.ViewData["SomeProperty"].ToString())) .Returns(Task.FromResult(0)); var viewEngine = new Mock(MockBehavior.Strict); viewEngine .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny(), /*isMainPage*/ false)) .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty())); viewEngine .Setup(v => v.FindView(It.IsAny(), It.IsAny(), /*isMainPage*/ false)) .Returns(ViewEngineResult.Found("SomeView", view.Object)); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); // Act var displayResult = helper.DisplayFor(expression: m => m.SomeProperty, additionalViewData: new { SomeProperty = "ViewDataValue" }); // Assert Assert.Equal("ViewDataValue", HtmlContentUtilities.HtmlContentToString(displayResult)); } [Fact] public void DisplayFor_UsesTemplateName() { // Arrange var model = new SomeModel { SomeProperty = "ModelValue" }; var viewEngine = new Mock(MockBehavior.Strict); viewEngine .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny(), /*isMainPage*/ false)) .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty())); viewEngine .Setup(v => v.FindView(It.IsAny(), "DisplayTemplates/SomeTemplate", /*isMainPage*/ false)) .Returns(ViewEngineResult.Found("SomeView", Mock.Of())) .Verifiable(); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); // Act var displayResult = helper.DisplayFor(expression: m => m.SomeProperty, templateName: "SomeTemplate"); // Assert viewEngine.Verify(); } [Fact] public void DisplayFor_UsesTemplateNameAndAdditionalViewData() { // Arrange var model = new SomeModel { SomeProperty = "ModelValue" }; var view = new Mock(); view.Setup(v => v.RenderAsync(It.IsAny())) .Callback((ViewContext v) => v.Writer.WriteAsync(v.ViewData["SomeProperty"].ToString())) .Returns(Task.FromResult(0)); var viewEngine = new Mock(MockBehavior.Strict); viewEngine .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny(), /*isMainPage*/ false)) .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty())); viewEngine .Setup(v => v.FindView(It.IsAny(), "DisplayTemplates/SomeTemplate", /*isMainPage*/ false)) .Returns(ViewEngineResult.Found("SomeView", view.Object)); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); // Act var displayResult = helper.DisplayFor( expression: m => m.SomeProperty, templateName: "SomeTemplate", additionalViewData: new { SomeProperty = "ViewDataValue" }); // Assert Assert.Equal("ViewDataValue", HtmlContentUtilities.HtmlContentToString(displayResult)); } [Fact] public void DisplayFor_EnumProperty_IStringLocalizedValue() { // Arrange var model = new StatusModel { Status = Status.Created }; var view = new Mock(); view.Setup(v => v.RenderAsync(It.IsAny())) .Callback((ViewContext v) => v.Writer.WriteAsync(v.ViewData.TemplateInfo.FormattedModelValue.ToString())) .Returns(Task.FromResult(0)); var viewEngine = new Mock(MockBehavior.Strict); viewEngine .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny(), /*isMainPage*/ false)) .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty())); viewEngine .Setup(v => v.FindView(It.IsAny(), "DisplayTemplates/Status", /*isMainPage*/ false)) .Returns(ViewEngineResult.Found("SomeView", view.Object)); var stringLocalizer = new Mock(MockBehavior.Strict); stringLocalizer .Setup(s => s["CreatedKey"]) .Returns((key) => { return new LocalizedString(key, "created from IStringLocalizer"); }); var stringLocalizerFactory = new Mock(); stringLocalizerFactory .Setup(s => s.Create(typeof(Status))) .Returns(stringLocalizer.Object); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object, stringLocalizerFactory.Object); // Act var displayResult = helper.DisplayFor(m => m.Status); // Assert Assert.Equal("created from IStringLocalizer", HtmlContentUtilities.HtmlContentToString(displayResult)); } [Fact] public void DisplayFor_EnumProperty_ResourceTypeLocalizedValue() { // Arrange var model = new StatusModel { Status = Status.Faulted }; var view = new Mock(); view.Setup(v => v.RenderAsync(It.IsAny())) .Callback((ViewContext v) => v.Writer.WriteAsync(v.ViewData.TemplateInfo.FormattedModelValue.ToString())) .Returns(Task.FromResult(0)); var viewEngine = new Mock(MockBehavior.Strict); viewEngine .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny(), /*isMainPage*/ false)) .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty())); viewEngine .Setup(v => v.FindView(It.IsAny(), "DisplayTemplates/Status", /*isMainPage*/ false)) .Returns(ViewEngineResult.Found("SomeView", view.Object)); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); // Act var displayResult = helper.DisplayFor(m => m.Status); // Assert Assert.Equal("Faulted from ResourceType", HtmlContentUtilities.HtmlContentToString(displayResult)); } [Fact] public void DisplayFor_UsesTemplateNameAndHtmlFieldName() { // Arrange var model = new SomeModel { SomeProperty = "ModelValue" }; var view = new Mock(); view.Setup(v => v.RenderAsync(It.IsAny())) .Callback((ViewContext v) => v.Writer.WriteAsync(v.ViewData.TemplateInfo.HtmlFieldPrefix)) .Returns(Task.FromResult(0)); var viewEngine = new Mock(MockBehavior.Strict); viewEngine .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny(), /*isMainPage*/ false)) .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty())); viewEngine .Setup(v => v.FindView(It.IsAny(), "DisplayTemplates/SomeTemplate", /*isMainPage*/ false)) .Returns(ViewEngineResult.Found("SomeView", view.Object)); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); // Act var displayResult = helper.DisplayFor( expression: m => m.SomeProperty, templateName: "SomeTemplate", htmlFieldName: "SomeField"); // Assert Assert.Equal("SomeField", HtmlContentUtilities.HtmlContentToString(displayResult)); } [Fact] public void DisplayForModel_UsesAdditionalViewData() { // Arrange var model = new SomeModel { SomeProperty = "ModelValue" }; var view = new Mock(); view.Setup(v => v.RenderAsync(It.IsAny())) .Callback((ViewContext v) => v.Writer.WriteAsync(v.ViewData["SomeProperty"].ToString())) .Returns(Task.FromResult(0)); var viewEngine = new Mock(MockBehavior.Strict); viewEngine .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny(), /*isMainPage*/ false)) .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty())); viewEngine .Setup(v => v.FindView(It.IsAny(), It.IsAny(), /*isMainPage*/ false)) .Returns(ViewEngineResult.Found("SomeView", view.Object)); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); // Act var displayResult = helper.DisplayForModel(additionalViewData: new { SomeProperty = "ViewDataValue" }); // Assert Assert.Equal("ViewDataValue", HtmlContentUtilities.HtmlContentToString(displayResult)); } [Fact] public void DisplayForModel_UsesTemplateName() { // Arrange var model = new SomeModel { SomeProperty = "ModelValue" }; var viewEngine = new Mock(MockBehavior.Strict); viewEngine .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny(), /*isMainPage*/ false)) .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty())); viewEngine .Setup(v => v.FindView(It.IsAny(), "DisplayTemplates/SomeTemplate", /*isMainPage*/ false)) .Returns(ViewEngineResult.Found("SomeView", Mock.Of())) .Verifiable(); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); // Act var displayResult = helper.DisplayForModel(templateName: "SomeTemplate"); // Assert viewEngine.Verify(); } [Fact] public void DisplayForModel_UsesTemplateNameAndAdditionalViewData() { // Arrange var model = new SomeModel { SomeProperty = "ModelValue" }; var view = new Mock(); view.Setup(v => v.RenderAsync(It.IsAny())) .Callback((ViewContext v) => v.Writer.WriteAsync(v.ViewData["SomeProperty"].ToString())) .Returns(Task.FromResult(0)); var viewEngine = new Mock(MockBehavior.Strict); viewEngine .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny(), /*isMainPage*/ false)) .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty())); viewEngine .Setup(v => v.FindView(It.IsAny(), "DisplayTemplates/SomeTemplate", /*isMainPage*/ false)) .Returns(ViewEngineResult.Found("SomeView", view.Object)); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); // Act var displayResult = helper.DisplayForModel( templateName: "SomeTemplate", additionalViewData: new { SomeProperty = "ViewDataValue" }); // Assert Assert.Equal("ViewDataValue", HtmlContentUtilities.HtmlContentToString(displayResult)); } [Fact] public void DisplayForModel_UsesTemplateNameAndHtmlFieldName() { // Arrange var model = new SomeModel { SomeProperty = "ModelValue" }; var view = new Mock(); view.Setup(v => v.RenderAsync(It.IsAny())) .Callback((ViewContext v) => v.Writer.WriteAsync(v.ViewData.TemplateInfo.HtmlFieldPrefix)) .Returns(Task.FromResult(0)); var viewEngine = new Mock(MockBehavior.Strict); viewEngine .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny(), /*isMainPage*/ false)) .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty())); viewEngine .Setup(v => v.FindView(It.IsAny(), "DisplayTemplates/SomeTemplate", /*isMainPage*/ false)) .Returns(ViewEngineResult.Found("SomeView", view.Object)); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); // Act var displayResult = helper.DisplayForModel( templateName: "SomeTemplate", htmlFieldName: "SomeField"); // Assert Assert.Equal("SomeField", HtmlContentUtilities.HtmlContentToString(displayResult)); } public class StatusResource { public static string FaultedKey { get { return "Faulted from ResourceType"; } } } public enum Status : byte { [Display(Name = "CreatedKey")] Created, [Display(Name = "FaultedKey", ResourceType = typeof(StatusResource))] Faulted, Done } public class FormatModel { [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "Value: {0}")] public Status FormatProperty { get; set; } public Status NonFormatProperty { get; set; } } private class SomeModel { public string SomeProperty { get; set; } } private class StatusModel { public Status Status { get; set; } } } }