diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/HtmlHelperPartialExtensions.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/HtmlHelperPartialExtensions.cs index de88d2a585..16c556b2a5 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Rendering/HtmlHelperPartialExtensions.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/HtmlHelperPartialExtensions.cs @@ -68,6 +68,98 @@ namespace Microsoft.AspNet.Mvc.Rendering return htmlHelper.PartialAsync(partialViewName, model, viewData: null); } + /// + /// Returns HTML markup for the specified partial view. + /// + /// The instance this method extends. + /// + /// The name of the partial view used to create the HTML markup. Must not be null. + /// + /// + /// Returns a new containing the created HTML. + /// + /// + /// This method synchronously calls and blocks on + /// + /// + public static HtmlString Partial( + [NotNull] this IHtmlHelper htmlHelper, + [NotNull] string partialViewName) + { + return Partial(htmlHelper, partialViewName, htmlHelper.ViewData.Model, viewData: null); + } + + /// + /// Returns HTML markup for the specified partial view. + /// + /// The instance this method extends. + /// + /// The name of the partial view used to create the HTML markup. Must not be null. + /// + /// A to pass into the partial view. + /// + /// Returns a new containing the created HTML. + /// + /// + /// This method synchronously calls and blocks on + /// + /// + public static HtmlString Partial( + [NotNull] this IHtmlHelper htmlHelper, + [NotNull] string partialViewName, + ViewDataDictionary viewData) + { + return Partial(htmlHelper, partialViewName, htmlHelper.ViewData.Model, viewData); + } + + /// + /// Returns HTML markup for the specified partial view. + /// + /// The instance this method extends. + /// + /// The name of the partial view used to create the HTML markup. Must not be null. + /// + /// A model to pass into the partial view. + /// + /// Returns a new containing the created HTML. + /// + /// + /// This method synchronously calls and blocks on + /// + /// + public static HtmlString Partial( + [NotNull] this IHtmlHelper htmlHelper, + [NotNull] string partialViewName, + object model) + { + return Partial(htmlHelper, partialViewName, model, viewData: null); + } + + /// + /// Returns HTML markup for the specified partial view. + /// + /// + /// The name of the partial view used to create the HTML markup. Must not be null. + /// + /// A model to pass into the partial view. + /// A to pass into the partial view. + /// + /// Returns a new containing the created HTML. + /// + /// + /// This method synchronously calls and blocks on + /// + /// + public static HtmlString Partial( + [NotNull] this IHtmlHelper htmlHelper, + [NotNull] string partialViewName, + object model, + ViewDataDictionary viewData) + { + var result = htmlHelper.PartialAsync(partialViewName, model, viewData); + return TaskHelper.WaitAndThrowIfFaulted(result); + } + /// /// Renders HTML markup for the specified partial view. /// diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultTemplatesUtilities.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultTemplatesUtilities.cs index c02f465f89..ec64aeb464 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultTemplatesUtilities.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultTemplatesUtilities.cs @@ -6,17 +6,15 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Globalization; using System.IO; -using System.Linq; using System.Threading.Tasks; using Microsoft.AspNet.Http; using Microsoft.AspNet.Mvc.ModelBinding; -using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Routing; using Microsoft.AspNet.Security.DataProtection; using Microsoft.Framework.OptionsModel; using Moq; -namespace Microsoft.AspNet.Mvc.Core +namespace Microsoft.AspNet.Mvc.Rendering { public class DefaultTemplatesUtilities { @@ -143,6 +141,12 @@ namespace Microsoft.AspNet.Mvc.Core return htmlHelper; } + public static string FormatOutput(IHtmlHelper helper, object model) + { + var metadata = helper.MetadataProvider.GetMetadataForType(() => model, model.GetType()); + return FormatOutput(metadata); + } + private static ICompositeViewEngine CreateViewEngine() { var view = new Mock(); diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/HtmlHelperDisplayTextTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/HtmlHelperDisplayTextTest.cs index 011df639d8..905b5cbd86 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/HtmlHelperDisplayTextTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/HtmlHelperDisplayTextTest.cs @@ -5,11 +5,11 @@ using System.Globalization; using Microsoft.AspNet.Mvc.ModelBinding; using Xunit; -namespace Microsoft.AspNet.Mvc.Core +namespace Microsoft.AspNet.Mvc.Rendering { /// - /// Test the and - /// methods. + /// Test the and + /// methods. /// public class HtmlHelperDisplayTextTest { diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/HtmlHelperPartialExtensionsTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/HtmlHelperPartialExtensionsTest.cs new file mode 100644 index 0000000000..1a4f644f14 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/HtmlHelperPartialExtensionsTest.cs @@ -0,0 +1,195 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading.Tasks; +using Microsoft.AspNet.Mvc.ModelBinding; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Mvc.Rendering +{ + public class HtmlHelperPartialExtensionsTest + { + public static TheoryData> PartialExtensionMethods + { + get + { + var vdd = new ViewDataDictionary(new EmptyModelMetadataProvider()); + return new TheoryData> + { + helper => helper.Partial("test"), + helper => helper.Partial("test", new object()), + helper => helper.Partial("test", vdd), + helper => helper.Partial("test", new object(), vdd) + }; + } + } + + [Theory] + [MemberData(nameof(PartialExtensionMethods))] + public void PartialMethods_DoesNotWrapThrownException(Func partialMethod) + { + // Arrange + var expected = new InvalidOperationException(); + var helper = new Mock(); + helper.Setup(h => h.PartialAsync("test", It.IsAny(), It.IsAny())) + .Callback(() => + { + // Workaround for compilation issue with Moq. + helper.ToString(); + throw expected; + }); + helper.SetupGet(h => h.ViewData) + .Returns(new ViewDataDictionary(new EmptyModelMetadataProvider())); + + // Act and Assert + var actual = Assert.Throws(() => partialMethod(helper.Object)); + Assert.Same(expected, actual); + } + + [Fact] + public void Partial_InvokesPartialAsyncWithCurrentModel() + { + // Arrange + var expected = new HtmlString("value"); + var model = new object(); + var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider()) + { + Model = model + }; + var helper = new Mock(MockBehavior.Strict); + helper.Setup(h => h.PartialAsync("test", model, null)) + .Returns(Task.FromResult(expected)) + .Verifiable(); + helper.SetupGet(h => h.ViewData) + .Returns(viewData); + + // Act + var actual = helper.Object.Partial("test"); + + // Assert + Assert.Same(expected, actual); + helper.Verify(); + } + + [Fact] + public void PartialWithModel_InvokesPartialAsyncWithPassedInModel() + { + // Arrange + var expected = new HtmlString("value"); + var model = new object(); + var helper = new Mock(MockBehavior.Strict); + helper.Setup(h => h.PartialAsync("test", model, null)) + .Returns(Task.FromResult(expected)) + .Verifiable(); + + // Act + var actual = helper.Object.Partial("test", model); + + // Assert + Assert.Same(expected, actual); + helper.Verify(); + } + + [Fact] + public void PartialWithViewData_InvokesPartialAsyncWithPassedInViewData() + { + // Arrange + var expected = new HtmlString("value"); + var model = new object(); + var passedInViewData = new ViewDataDictionary(new EmptyModelMetadataProvider()); + var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider()) + { + Model = model + }; + var helper = new Mock(MockBehavior.Strict); + helper.Setup(h => h.PartialAsync("test", model, passedInViewData)) + .Returns(Task.FromResult(expected)) + .Verifiable(); + helper.SetupGet(h => h.ViewData) + .Returns(viewData); + + // Act + var actual = helper.Object.Partial("test", passedInViewData); + + // Assert + Assert.Same(expected, actual); + helper.Verify(); + } + + [Fact] + public void PartialWithViewDataAndModel_InvokesPartialAsyncWithPassedInViewDataAndModel() + { + // Arrange + var expected = new HtmlString("value"); + var passedInModel = new object(); + var passedInViewData = new ViewDataDictionary(new EmptyModelMetadataProvider()); + var helper = new Mock(MockBehavior.Strict); + helper.Setup(h => h.PartialAsync("test", passedInModel, passedInViewData)) + .Returns(Task.FromResult(expected)) + .Verifiable(); + + // Act + var actual = helper.Object.Partial("test", passedInModel, passedInViewData); + + // Assert + Assert.Same(expected, actual); + helper.Verify(); + } + + [Fact] + public void Partial_InvokesAndRendersPartialAsyncOnHtmlHelperOfT() + { + // Arrange + var model = new TestModel(); + var helper = DefaultTemplatesUtilities.GetHtmlHelper(model); + var expected = DefaultTemplatesUtilities.FormatOutput(helper, model); + + // Act + var actual = helper.Partial("some-partial"); + + // Assert + Assert.Equal(expected, actual.ToString()); + } + + [Fact] + public void PartialWithModel_InvokesAndRendersPartialAsyncOnHtmlHelperOfT() + { + // Arrange + var model = new TestModel(); + var helper = DefaultTemplatesUtilities.GetHtmlHelper(); + var expected = DefaultTemplatesUtilities.FormatOutput(helper, model); + + // Act + var actual = helper.Partial("some-partial", model); + + // Assert + Assert.Equal(expected, actual.ToString()); + } + + [Fact] + public void PartialWithViewData_InvokesAndRendersPartialAsyncOnHtmlHelperOfT() + { + // Arrange + var model = new TestModel(); + var helper = DefaultTemplatesUtilities.GetHtmlHelper(model); + var viewData = new ViewDataDictionary(helper.MetadataProvider); + var expected = DefaultTemplatesUtilities.FormatOutput(helper, model); + + // Act + var actual = helper.Partial("some-partial", viewData); + + // Assert + Assert.Equal(expected, actual.ToString()); + } + + private sealed class TestModel + { + public override string ToString() + { + return "test-model-content"; + } + } + } +} \ No newline at end of file