From c3e92a91b54072c7d5d2594bbaa56188f270af51 Mon Sep 17 00:00:00 2001 From: Mole Date: Mon, 19 Oct 2020 14:03:51 +0200 Subject: [PATCH 1/4] Mingrate RenderModelBinderTests --- .../ModelBinders/RenderModelBinderTests.cs | 175 +++++++++++++++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 1 - .../Web/Mvc/RenderModelBinderTests.cs | 181 ------------------ .../ModelBinders/ContentModelBinder.cs | 5 +- 4 files changed, 178 insertions(+), 184 deletions(-) create mode 100644 src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ModelBinders/RenderModelBinderTests.cs delete mode 100644 src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ModelBinders/RenderModelBinderTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ModelBinders/RenderModelBinderTests.cs new file mode 100644 index 0000000000..501c10551d --- /dev/null +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ModelBinders/RenderModelBinderTests.cs @@ -0,0 +1,175 @@ +using System; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Routing; +using Moq; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Web.Common.ModelBinders; +using Umbraco.Web.Models; + +namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.ModelBinders +{ + [TestFixture] + public class RenderModelBinderTests + { + private ContentModelBinder _contentModelBinder; + [SetUp] + public void SetUp() + { + _contentModelBinder = new ContentModelBinder(); + } + + [Test] + [TestCase(typeof(IPublishedContent), false)] + [TestCase(typeof(ContentModel), false)] + [TestCase(typeof(MyContent), false)] + [TestCase(typeof(ContentModel), false)] + [TestCase(typeof(MyOtherContent), true)] + [TestCase(typeof(MyCustomContentModel), true)] + [TestCase(typeof(IContentModel), true)] + public void Returns_Binder_For_IPublishedContent_And_IRenderModel(Type testType, bool expectNull) + { + var binderProvider = new ContentModelBinderProvider(); + var contextMock = new Mock(); + contextMock.Setup(x => x.Metadata).Returns(new EmptyModelMetadataProvider().GetMetadataForType(testType)); + + var found = binderProvider.GetBinder(contextMock.Object); + if (expectNull) + { + Assert.IsNull(found); + } + else + { + Assert.IsNotNull(found); + } + } + + [Test] + public void BindModel_Null_Source_Returns_Null() + { + var bindingContext = new DefaultModelBindingContext(); + _contentModelBinder.BindModelAsync(bindingContext, null, typeof(MyContent)); + Assert.IsNull(bindingContext.Result.Model); + } + + [Test] + public void BindModel_Returns_If_Same_Type() + { + var content = new MyContent(Mock.Of()); + var bindingContext = new DefaultModelBindingContext(); + + _contentModelBinder.BindModelAsync(bindingContext, content, typeof(MyContent)); + + Assert.AreSame(content, bindingContext.Result.Model); + } + + [Test] + public void BindModel_RenderModel_To_IPublishedContent() + { + var content = new MyContent(Mock.Of()); + var renderModel = new ContentModel(content); + + var bindingContext = new DefaultModelBindingContext(); + _contentModelBinder.BindModelAsync(bindingContext, renderModel, typeof(IPublishedContent)); + + Assert.AreSame(content, bindingContext.Result.Model); + } + + [Test] + public void BindModel_IPublishedContent_To_RenderModel() + { + var content = new MyContent(Mock.Of()); + var bindingContext = new DefaultModelBindingContext(); + + _contentModelBinder.BindModelAsync(bindingContext, content, typeof(ContentModel)); + var bound = (IContentModel) bindingContext.Result.Model; + + Assert.AreSame(content, bound.Content); + } + + [Test] + public void BindModel_IPublishedContent_To_Generic_RenderModel() + { + var content = new MyContent(Mock.Of()); + var bindingContext = new DefaultModelBindingContext(); + + _contentModelBinder.BindModelAsync(bindingContext, content, typeof(ContentModel)); + var bound = (IContentModel) bindingContext.Result.Model; + + Assert.AreSame(content, bound.Content); + } + + [Test] + public void No_DataToken_Returns_Null() + { + var content = new MyContent(Mock.Of()); + var bindingContext = CreateBindingContext(typeof(ContentModel), false, content); + + _contentModelBinder.BindModelAsync(bindingContext); + + Assert.IsNull(bindingContext.Result.Model); + } + + [Test] + public void Invalid_DataToken_Model_Type_Returns_Null() + { + var bindingContext = CreateBindingContext(typeof(IPublishedContent), source: "Hello"); + _contentModelBinder.BindModelAsync(bindingContext); + Assert.IsNull(bindingContext.Result.Model); + } + + [Test] + public void IPublishedContent_DataToken_Model_Type_Uses_DefaultImplementation() + { + var content = new MyContent(Mock.Of()); + var bindingContext = CreateBindingContext(typeof(MyContent), source: content); + + _contentModelBinder.BindModelAsync(bindingContext); + + Assert.AreEqual(content, bindingContext.Result.Model); + } + + private ModelBindingContext CreateBindingContext(Type modelType, bool withUmbracoDataToken = true, object source = null) + { + var httpContext = new DefaultHttpContext(); + var routeData = new RouteData(); + if (withUmbracoDataToken) + routeData.DataTokens.Add(Constants.Web.UmbracoDataToken, source); + + var actionContext = new ActionContext(httpContext, routeData, new ActionDescriptor()); + var metadataProvider = new EmptyModelMetadataProvider(); + var routeValueDictionary = new RouteValueDictionary(); + var valueProvider = new RouteValueProvider(BindingSource.Path, routeValueDictionary); + return new DefaultModelBindingContext + { + ActionContext = actionContext, + ModelMetadata = metadataProvider.GetMetadataForType(modelType), + ModelName = modelType.Name, + ValueProvider = valueProvider, + }; + } + + public class MyCustomContentModel : ContentModel + { + public MyCustomContentModel(IPublishedContent content) + : base(content) + { } + } + + public class MyOtherContent + { + + } + + public class MyContent : PublishedContentWrapped + { + public MyContent(IPublishedContent content) : base(content) + { + } + } + } +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 616bfa96b6..5d00eebfac 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -238,7 +238,6 @@ - diff --git a/src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs b/src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs deleted file mode 100644 index aa94272964..0000000000 --- a/src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs +++ /dev/null @@ -1,181 +0,0 @@ -using System.Collections.Generic; -using System.Globalization; -using System.Web; -using System.Web.Mvc; -using System.Web.Routing; -using Moq; -using NUnit.Framework; -using Umbraco.Core.Models; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Tests.Common; -using Umbraco.Tests.TestHelpers.Stubs; -using Umbraco.Web.Models; -using Umbraco.Web.Mvc; -using Current = Umbraco.Web.Composing.Current; - -namespace Umbraco.Tests.Web.Mvc -{ - [TestFixture] - public class RenderModelBinderTests - { - [SetUp] - public void SetUp() - { - Current.UmbracoContextAccessor = new TestUmbracoContextAccessor(); - } - - [TearDown] - public void TearDown() - { - Current.Reset(); - } - - [Test] - public void Returns_Binder_For_IPublishedContent_And_IRenderModel() - { - var binder = ContentModelBinder.Instance; - var found = binder.GetBinder(typeof (IPublishedContent)); - Assert.IsNotNull(found); - found = binder.GetBinder(typeof(ContentModel)); - Assert.IsNotNull(found); - found = binder.GetBinder(typeof(MyContent)); - Assert.IsNotNull(found); - found = binder.GetBinder(typeof(ContentModel)); - Assert.IsNotNull(found); - - found = binder.GetBinder(typeof(MyOtherContent)); - Assert.IsNull(found); - found = binder.GetBinder(typeof(MyCustomContentModel)); - Assert.IsNull(found); - found = binder.GetBinder(typeof(IContentModel)); - Assert.IsNull(found); - } - - [Test] - public void BindModel_Null_Source_Returns_Null() - { - Assert.IsNull(ContentModelBinder.BindModel(null, typeof(MyContent))); - } - - [Test] - public void BindModel_Returns_If_Same_Type() - { - var content = new MyContent(Mock.Of()); - var bound = ContentModelBinder.BindModel(content, typeof (IPublishedContent)); - Assert.AreSame(content, bound); - } - - [Test] - public void BindModel_RenderModel_To_IPublishedContent() - { - var content = new MyContent(Mock.Of()); - var renderModel = new ContentModel(content); - var bound = ContentModelBinder.BindModel(renderModel, typeof(IPublishedContent)); - Assert.AreSame(content, bound); - } - - [Test] - public void BindModel_IPublishedContent_To_RenderModel() - { - var content = new MyContent(Mock.Of()); - var bound = (IContentModel)ContentModelBinder.BindModel(content, typeof(ContentModel)); - Assert.AreSame(content, bound.Content); - } - - [Test] - public void BindModel_IPublishedContent_To_Generic_RenderModel() - { - var content = new MyContent(Mock.Of()); - var bound = (IContentModel)ContentModelBinder.BindModel(content, typeof(ContentModel)); - Assert.AreSame(content, bound.Content); - } - - [Test] - public void No_DataToken_Returns_Null() - { - var binder = ContentModelBinder.Instance; - var routeData = new RouteData(); - var result = binder.BindModel(new ControllerContext(Mock.Of(), routeData, Mock.Of()), - new ModelBindingContext()); - - Assert.IsNull(result); - } - - [Test] - public void Invalid_DataToken_Model_Type_Returns_Null() - { - var binder = ContentModelBinder.Instance; - var routeData = new RouteData(); - routeData.DataTokens[Core.Constants.Web.UmbracoDataToken] = "hello"; - - //the value provider is the default implementation - var valueProvider = new Mock(); - //also IUnvalidatedValueProvider - var invalidatedValueProvider = valueProvider.As(); - invalidatedValueProvider.Setup(x => x.GetValue(It.IsAny(), It.IsAny())).Returns(() => - new ValueProviderResult(null, "", CultureInfo.CurrentCulture)); - - var controllerCtx = new ControllerContext( - Mock.Of(http => http.Items == new Dictionary()), - routeData, - Mock.Of()); - - var result = binder.BindModel(controllerCtx, - new ModelBindingContext - { - ValueProvider = valueProvider.Object, - ModelMetadata = new ModelMetadata(new EmptyModelMetadataProvider(), null, () => null, typeof(IPublishedContent), "content") - }); - - Assert.IsNull(result); - } - - [Test] - public void IPublishedContent_DataToken_Model_Type_Uses_DefaultImplementation() - { - var content = new MyContent(Mock.Of()); - var binder = ContentModelBinder.Instance; - var routeData = new RouteData(); - routeData.DataTokens[Core.Constants.Web.UmbracoDataToken] = content; - - //the value provider is the default implementation - var valueProvider = new Mock(); - //also IUnvalidatedValueProvider - var invalidatedValueProvider = valueProvider.As(); - invalidatedValueProvider.Setup(x => x.GetValue(It.IsAny(), It.IsAny())).Returns(() => - new ValueProviderResult(content, "content", CultureInfo.CurrentCulture)); - - var controllerCtx = new ControllerContext( - Mock.Of(http => http.Items == new Dictionary()), - routeData, - Mock.Of()); - var result = binder.BindModel(controllerCtx, - new ModelBindingContext - { - ValueProvider = valueProvider.Object, - ModelMetadata = new ModelMetadata(new EmptyModelMetadataProvider(), null, () => null, typeof(IPublishedContent), "content") - }); - - Assert.AreEqual(content, result); - } - - public class MyCustomContentModel : ContentModel - { - public MyCustomContentModel(IPublishedContent content) - : base(content) - { } - } - - public class MyOtherContent - { - - } - - public class MyContent : PublishedContentWrapped - { - public MyContent(IPublishedContent content) : base(content) - { - } - } - } -} diff --git a/src/Umbraco.Web.Common/ModelBinders/ContentModelBinder.cs b/src/Umbraco.Web.Common/ModelBinders/ContentModelBinder.cs index 43d9df4586..3c2da057f7 100644 --- a/src/Umbraco.Web.Common/ModelBinders/ContentModelBinder.cs +++ b/src/Umbraco.Web.Common/ModelBinders/ContentModelBinder.cs @@ -27,7 +27,7 @@ namespace Umbraco.Web.Common.ModelBinders // No need for type checks to ensure we have the appropriate binder, as in .NET Core this is handled in the provider, // in this case ContentModelBinderProvider. - // Being defensice though.... if for any reason the model is not either IContentModel or IPublishedContent, + // Being defensive though.... if for any reason the model is not either IContentModel or IPublishedContent, // then we return since those are the only types this binder is dealing with. if (source is IContentModel == false && source is IPublishedContent == false) { @@ -36,7 +36,7 @@ namespace Umbraco.Web.Common.ModelBinders BindModelAsync(bindingContext, source, bindingContext.ModelType); return Task.CompletedTask; - } + } // source is the model that we have // modelType is the type of the model that we need to bind to @@ -58,6 +58,7 @@ namespace Umbraco.Web.Common.ModelBinders var sourceType = source.GetType(); if (sourceType.Inherits(modelType)) // includes == { + bindingContext.Result = ModelBindingResult.Success(source); return Task.CompletedTask; } From 5f114e72285ca20cbec0ea82477cadae0fe8f175 Mon Sep 17 00:00:00 2001 From: Mole Date: Mon, 19 Oct 2020 14:34:08 +0200 Subject: [PATCH 2/4] Align ContentModelBinderTests to new behaviour --- .../ModelBinders/ContentModelBinderTests.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ModelBinders/ContentModelBinderTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ModelBinders/ContentModelBinderTests.cs index 0ad8e2c421..b414e49e95 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ModelBinders/ContentModelBinderTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ModelBinders/ContentModelBinderTests.cs @@ -45,17 +45,18 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.ModelBinders } [Test] - public void Does_Not_Bind_Model_When_Source_Type_Matches_Model_Type() + public void BindModel_Returns_If_Same_Type() { // Arrange - var bindingContext = CreateBindingContext(typeof(ContentModel), source: new ContentModel(CreatePublishedContent())); + var content = new ContentModel(CreatePublishedContent()); + var bindingContext = CreateBindingContext(typeof(ContentModel), source: content); var binder = new ContentModelBinder(); // Act binder.BindModelAsync(bindingContext); // Assert - Assert.False(bindingContext.Result.IsModelSet); + Assert.AreSame(content, bindingContext.Result.Model); } [Test] From d315d2120e88edadd137695e1706f63ef581706c Mon Sep 17 00:00:00 2001 From: Mole Date: Tue, 20 Oct 2020 13:41:21 +0200 Subject: [PATCH 3/4] Migrate UmbracoViewPageTests --- .../Views/UmbracoViewPageTests.cs | 358 ++++++++++++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 1 - .../Web/Mvc/UmbracoViewPageTests.cs | 466 ------------------ 3 files changed, 358 insertions(+), 467 deletions(-) create mode 100644 src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Views/UmbracoViewPageTests.cs delete mode 100644 src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Views/UmbracoViewPageTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Views/UmbracoViewPageTests.cs new file mode 100644 index 0000000000..2bc8a53cc1 --- /dev/null +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Views/UmbracoViewPageTests.cs @@ -0,0 +1,358 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using NUnit.Framework; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Web.Common.AspNetCore; +using Umbraco.Web.Models; + +namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.Views +{ + [TestFixture] + public class UmbracoViewPageTests + { + #region RenderModel To ... + [Test] + public void RenderModel_To_RenderModel() + { + var content = new ContentType1(null); + var model = new ContentModel(content); + var view = new RenderModelTestPage(); + var viewData = GetViewDataDictionary(model); + view.ViewData = viewData; + + Assert.AreSame(model, view.Model); + } + + [Test] + public void RenderModel_ContentType1_To_ContentType1() + { + var content = new ContentType1(null); + var view = new ContentType1TestPage(); + var viewData = GetViewDataDictionary(content); + + view.ViewData = viewData; + + Assert.IsInstanceOf(view.Model); + } + + [Test] + public void RenderModel_ContentType2_To_ContentType1() + { + var content = new ContentType2(null); + var view = new ContentType1TestPage(); + + var viewData = GetViewDataDictionary(content); + view.ViewData = viewData; + + Assert.IsInstanceOf(view.Model); + } + + // [Test] + // public void RenderModel_ContentType1_To_ContentType2() + // { + // // everything is strongly typed, so I'm not even allowed to try and set the ViewData with the wrong type + // var content = new ContentType1(null); + // var model = new ContentModel(content); + // var view = new ContentType2TestPage(); + // var viewData = new ViewDataDictionary(model); + // + // view.ViewContext = GetViewContext(); + // + // Assert.Throws(() => view.SetViewDataX(viewData)); + // } + + [Test] + public void RenderModel_ContentType1_To_RenderModelOf_ContentType1() + { + var content = new ContentType1(null); + var model = new ContentModel(content); + var view = new RenderModelOfContentType1TestPage(); + var viewData = GetViewDataDictionary>(model); + + view.ViewData = viewData; + + Assert.IsInstanceOf>(view.Model); + Assert.IsInstanceOf(view.Model.Content); + } + + [Test] + public void RenderModel_ContentType2_To_RenderModelOf_ContentType1() + { + var content = new ContentType2(null); + var model = new ContentModel(content); + var view = new RenderModelOfContentType1TestPage(); + var viewData = GetViewDataDictionary>(model); + view.ViewData = viewData; + + Assert.IsInstanceOf>(view.Model); + Assert.IsInstanceOf(view.Model.Content); + } + + // [Test] + // public void RenderModel_ContentType1_To_RenderModelOf_ContentType2() + // { + // // everything is strongly typed, so I'm not even allowed to try and create the ContentModel with the wrong type + // var content = new ContentType1(null); + // var model = new ContentModel(content); + // var view = new RenderModelOfContentType2TestPage(); + // var viewData = new ViewDataDictionary(model); + // + // view.ViewContext = GetViewContext(); + // + // Assert.Throws(() => view.SetViewDataX(viewData)); + // } + + #endregion + + #region RenderModelOf To ... + + [Test] + public void RenderModelOf_ContentType1_To_RenderModel() + { + var content = new ContentType1(null); + var model = new ContentModel(content); + var view = new RenderModelTestPage(); + var viewData = GetViewDataDictionary(model); + + view.ViewData = viewData; + + Assert.AreSame(model, view.Model); + } + + // [Test] + // public void RenderModelOf_ContentType1_To_ContentType1() + // { + // // Can't create viewdata with ContentType1 from ContentModel because it doesn't actually inherit ContentType1 + // // And can't set viewdata from ContentModel because the page expects ContentType1 and not ContentModel + // // And if I change it the test will be the same as RenderModel_ContentType1_To_ContentType1 + // var content = new ContentType1(null); + // var model = new ContentModel(content); + // var view = new ContentType1TestPage(); + // var viewData = GetViewDataDictionary>(model); + // + // view.ViewData = viewData; + // + // Assert.IsInstanceOf(view.Model); + // } + + // [Test] + // public void RenderModelOf_ContentType2_To_ContentType1() + // { + // // Same issue as above test + // var content = new ContentType2(null); + // var model = new ContentModel(content); + // var view = new ContentType1TestPage(); + // var viewData = new ViewDataDictionary(model); + // + // view.ViewContext = GetViewContext(); + // view.SetViewDataX(viewData); + // + // Assert.IsInstanceOf(view.Model); + // } + + // [Test] + // public void RenderModelOf_ContentType1_To_ContentType2() + // { + // // Same issue as above, and as RenderModel1_ContentType1_To_ContentType2 + // var content = new ContentType1(null); + // var model = new ContentModel(content); + // var view = new ContentType2TestPage(); + // var viewData = new ViewDataDictionary(model); + // + // view.ViewContext = GetViewContext(); + // Assert.Throws(() => view.SetViewDataX(viewData)); + // } + + // [Test] + // public void RenderModelOf_ContentType1_To_RenderModelOf_ContentType1() + // { + // // It's the same as RenderModel_ContentType1_To_RenderModelOf_ContentType1 + // var content = new ContentType1(null); + // var model = new ContentModel(content); + // var view = new RenderModelOfContentType1TestPage(); + // var viewData = GetViewDataDictionary>(model); + // + // view.ViewData = viewData; + // + // Assert.IsInstanceOf>(view.Model); + // Assert.IsInstanceOf(view.Model.Content); + // } + + // [Test] + // public void RenderModelOf_ContentType2_To_RenderModelOf_ContentType1() + // { + // // Same as RenderModel_ContentType2_To_RenderModelOf_ContentType1 after merge + // var content = new ContentType2(null); + // var model = new ContentModel(content); + // var view = new RenderModelOfContentType1TestPage(); + // var viewData = GetViewDataDictionary>(model); + // + // view.ViewData = viewData; + // + // Assert.IsInstanceOf>(view.Model); + // Assert.IsInstanceOf(view.Model.Content); + // } + + // [Test] + // public void RenderModelOf_ContentType1_To_RenderModelOf_ContentType2() + // { + // // Same issue as RenderModel1_ContentType1_To_ContentType2 + // var content = new ContentType1(null); + // var model = new ContentModel(content); + // var view = new RenderModelOfContentType2TestPage(); + // var viewData = new ViewDataDictionary(model); + // + // view.ViewContext = GetViewContext(); + // Assert.Throws(() => view.SetViewDataX(viewData)); + // } + + #endregion + + #region ContentType To ... + + // [Test] + // public void ContentType1_To_RenderModel() + // { + // // ContentType1 cannot be sat as ViewData because ContentModel is expected + // var content = new ContentType1(null); + // var view = new RenderModelTestPage(); + // + // var viewData = GetViewDataDictionary(content); + // + // view.ViewData = viewData; + // + // Assert.IsInstanceOf(view.Model); + // } + + // [Test] + // public void ContentType1_To_RenderModelOf_ContentType1() + // { + // // same as above but with ContentModel instead of ContentModel + // var content = new ContentType1(null); + // var view = new RenderModelOfContentType1TestPage(); + // + // var viewData = GetViewDataDictionary(content); + // view.ViewData = viewData; + // + // Assert.IsInstanceOf>(view.Model); + // Assert.IsInstanceOf(view.Model.Content); + // } + + // [Test] + // public void ContentType2_To_RenderModelOf_ContentType1() + // { + // // Same as above but with ContentModel + // var content = new ContentType2(null); + // var view = new RenderModelOfContentType1TestPage(); + // var viewData = GetViewDataDictionary(content); + // + // view.ViewData = viewData; + // + // Assert.IsInstanceOf>(view.Model); + // Assert.IsInstanceOf(view.Model.Content); + // } + + // [Test] + // public void ContentType1_To_RenderModelOf_ContentType2() + // { + // // Same as above, and as RenderModel1_ContentType1_To_ContentType2 + // var content = new ContentType1(null); + // var view = new RenderModelOfContentType2TestPage(); + // var viewData = new ViewDataDictionary(content); + // + // view.ViewContext = GetViewContext(); + // Assert.Throws(() =>view.SetViewDataX(viewData)); + // } + + // [Test] + // public void ContentType1_To_ContentType1() + // { + // // Same as ContentType1_To_ContentType1 + // var content = new ContentType1(null); + // var view = new ContentType1TestPage(); + // var viewdata = GetViewDataDictionary(content); + // + // view.ViewData = viewdata; + // + // Assert.IsInstanceOf(view.Model); + // } + + // [Test] + // public void ContentType1_To_ContentType2() + // { + // // Same issue as RenderModel1_ContentType1_To_ContentType2 + // var content = new ContentType1(null); + // var view = new ContentType2TestPage(); + // var viewData = new ViewDataDictionary(content); + // + // view.ViewContext = GetViewContext(); + // Assert.Throws(() => view.SetViewDataX(viewData)); + // } + + // [Test] + // public void ContentType2_To_ContentType1() + // { + // // Will be the same as RenderModel_ContentType2_To_ContentType1 after merge + // var content = new ContentType2(null); + // var view = new ContentType1TestPage(); + // var viewData = new ViewDataDictionary(content); + // + // view.ViewContext = GetViewContext(); + // view.SetViewDataX(viewData); + // + // Assert.IsInstanceOf(view.Model); + // } + + #endregion + + #region Test helpers methods + + private ViewDataDictionary GetViewDataDictionary(object model) + { + var sourceViewDataDictionary = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary()); + return new ViewDataDictionary(sourceViewDataDictionary, model); + } + + + #endregion + + #region Test elements + + public class ContentType1 : PublishedContentWrapped + { + public ContentType1(IPublishedContent content) : base(content) {} + } + + public class ContentType2 : ContentType1 + { + public ContentType2(IPublishedContent content) : base(content) { } + } + + public class TestPage : UmbracoViewPage + { + public override Task ExecuteAsync() + { + throw new NotImplementedException(); + } + } + + public class RenderModelTestPage : TestPage + { } + + public class ContentType1TestPage : TestPage + { } + + public class ContentType2TestPage : TestPage + { } + + public class RenderModelOfContentType1TestPage : TestPage> + { } + + public class RenderModelOfContentType2TestPage : TestPage> + { } + + #endregion + } +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 5d00eebfac..fa7cd02c31 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -239,7 +239,6 @@ - diff --git a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs deleted file mode 100644 index c7ab297076..0000000000 --- a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs +++ /dev/null @@ -1,466 +0,0 @@ -using System; -using System.Globalization; -using System.Web.Mvc; -using System.Web.Routing; -using Microsoft.Extensions.Logging.Abstractions; -using Moq; -using NUnit.Framework; -using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Configuration.Models; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.Services; -using Umbraco.Tests.Common; -using Umbraco.Tests.LegacyXmlPublishedCache; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.Testing; -using Umbraco.Web; -using Umbraco.Web.Composing; -using Umbraco.Web.Models; -using Umbraco.Web.Mvc; -using Umbraco.Web.Routing; -using Umbraco.Web.Security; - -namespace Umbraco.Tests.Web.Mvc -{ - [TestFixture] - [UmbracoTest(WithApplication = true)] - public class UmbracoViewPageTests : UmbracoTestBase - { - private XmlPublishedSnapshotService _service; - - [TearDown] - public override void TearDown() - { - if (_service == null) return; - _service.Dispose(); - _service = null; - } - - #region RenderModel To ... - - [Test] - public void RenderModel_To_RenderModel() - { - var content = new ContentType1(null); - var model = new ContentModel(content); - var view = new RenderModelTestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.AreSame(model, view.Model); - } - - [Test] - public void RenderModel_ContentType1_To_ContentType1() - { - var content = new ContentType1(null); - var model = new ContentModel(content); - var view = new ContentType1TestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.IsInstanceOf(view.Model); - } - - [Test] - public void RenderModel_ContentType2_To_ContentType1() - { - var content = new ContentType2(null); - var model = new ContentModel(content); - var view = new ContentType1TestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.IsInstanceOf(view.Model); - } - - [Test] - public void RenderModel_ContentType1_To_ContentType2() - { - var content = new ContentType1(null); - var model = new ContentModel(content); - var view = new ContentType2TestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - - Assert.Throws(() => view.SetViewDataX(viewData)); - } - - [Test] - public void RenderModel_ContentType1_To_RenderModelOf_ContentType1() - { - var content = new ContentType1(null); - var model = new ContentModel(content); - var view = new RenderModelOfContentType1TestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.IsInstanceOf>(view.Model); - Assert.IsInstanceOf(view.Model.Content); - } - - [Test] - public void RenderModel_ContentType2_To_RenderModelOf_ContentType1() - { - var content = new ContentType2(null); - var model = new ContentModel(content); - var view = new RenderModelOfContentType1TestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.IsInstanceOf>(view.Model); - Assert.IsInstanceOf(view.Model.Content); - } - - [Test] - public void RenderModel_ContentType1_To_RenderModelOf_ContentType2() - { - var content = new ContentType1(null); - var model = new ContentModel(content); - var view = new RenderModelOfContentType2TestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - - Assert.Throws(() => view.SetViewDataX(viewData)); - } - - #endregion - - #region RenderModelOf To ... - - [Test] - public void RenderModelOf_ContentType1_To_RenderModel() - { - var content = new ContentType1(null); - var model = new ContentModel(content); - var view = new RenderModelTestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.AreSame(model, view.Model); - } - - [Test] - public void RenderModelOf_ContentType1_To_ContentType1() - { - var content = new ContentType1(null); - var model = new ContentModel(content); - var view = new ContentType1TestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.IsInstanceOf(view.Model); - } - - [Test] - public void RenderModelOf_ContentType2_To_ContentType1() - { - var content = new ContentType2(null); - var model = new ContentModel(content); - var view = new ContentType1TestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.IsInstanceOf(view.Model); - } - - [Test] - public void RenderModelOf_ContentType1_To_ContentType2() - { - var content = new ContentType1(null); - var model = new ContentModel(content); - var view = new ContentType2TestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - Assert.Throws(() => view.SetViewDataX(viewData)); - } - - [Test] - public void RenderModelOf_ContentType1_To_RenderModelOf_ContentType1() - { - var content = new ContentType1(null); - var model = new ContentModel(content); - var view = new RenderModelOfContentType1TestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.IsInstanceOf>(view.Model); - Assert.IsInstanceOf(view.Model.Content); - } - - [Test] - public void RenderModelOf_ContentType2_To_RenderModelOf_ContentType1() - { - var content = new ContentType2(null); - var model = new ContentModel(content); - var view = new RenderModelOfContentType1TestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.IsInstanceOf>(view.Model); - Assert.IsInstanceOf(view.Model.Content); - } - - [Test] - public void RenderModelOf_ContentType1_To_RenderModelOf_ContentType2() - { - var content = new ContentType1(null); - var model = new ContentModel(content); - var view = new RenderModelOfContentType2TestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - Assert.Throws(() => view.SetViewDataX(viewData)); - } - - #endregion - - #region ContentType To ... - - [Test] - public void ContentType1_To_RenderModel() - { - var content = new ContentType1(null); - var view = new RenderModelTestPage(); - var viewData = new ViewDataDictionary(content); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.IsInstanceOf(view.Model); - } - - [Test] - public void ContentType1_To_RenderModelOf_ContentType1() - { - var content = new ContentType1(null); - var view = new RenderModelOfContentType1TestPage(); - var viewData = new ViewDataDictionary(content); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.IsInstanceOf>(view.Model); - Assert.IsInstanceOf(view.Model.Content); - } - - [Test] - public void ContentType2_To_RenderModelOf_ContentType1() - { - var content = new ContentType2(null); - var view = new RenderModelOfContentType1TestPage(); - var viewData = new ViewDataDictionary(content); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.IsInstanceOf>(view.Model); - Assert.IsInstanceOf(view.Model.Content); - } - - [Test] - public void ContentType1_To_RenderModelOf_ContentType2() - { - var content = new ContentType1(null); - var view = new RenderModelOfContentType2TestPage(); - var viewData = new ViewDataDictionary(content); - - view.ViewContext = GetViewContext(); - Assert.Throws(() =>view.SetViewDataX(viewData)); - } - - [Test] - public void ContentType1_To_ContentType1() - { - var content = new ContentType1(null); - var view = new ContentType1TestPage(); - var viewData = new ViewDataDictionary(content); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.IsInstanceOf(view.Model); - } - - [Test] - public void ContentType1_To_ContentType2() - { - var content = new ContentType1(null); - var view = new ContentType2TestPage(); - var viewData = new ViewDataDictionary(content); - - view.ViewContext = GetViewContext(); - Assert.Throws(() => view.SetViewDataX(viewData)); - } - - [Test] - public void ContentType2_To_ContentType1() - { - var content = new ContentType2(null); - var view = new ContentType1TestPage(); - var viewData = new ViewDataDictionary(content); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.IsInstanceOf(view.Model); - } - - #endregion - - #region Test elements - - public class TestPage : UmbracoViewPage - { - public override void Execute() - { - throw new NotImplementedException(); - } - - public void SetViewDataX(ViewDataDictionary viewData) - { - SetViewData(viewData); - } - } - - public class RenderModelTestPage : TestPage - { } - - public class RenderModelOfContentType1TestPage : TestPage> - { } - - public class RenderModelOfContentType2TestPage : TestPage> - { } - - public class ContentType1TestPage : TestPage - { } - - public class ContentType2TestPage : TestPage - { } - - public class ContentType1 : PublishedContentWrapped - { - public ContentType1(IPublishedContent content) : base(content) {} - } - - public class ContentType2 : ContentType1 - { - public ContentType2(IPublishedContent content) : base(content) { } - } - - #endregion - - #region Test helpers - - ServiceContext GetServiceContext() - { - return TestObjects.GetServiceContextMock(); - } - - ViewContext GetViewContext() - { - var umbracoContext = GetUmbracoContext("/dang", 0); - - var webRoutingSettings = new WebRoutingSettings(); - var publishedRouter = BaseWebTest.CreatePublishedRouter(webRoutingSettings); - var frequest = publishedRouter.CreateRequest(umbracoContext, new Uri("http://localhost/dang")); - - frequest.Culture = CultureInfo.InvariantCulture; - umbracoContext.PublishedRequest = frequest; - - var context = new ViewContext(); - context.RouteData = new RouteData(); - context.RouteData.DataTokens.Add(Core.Constants.Web.UmbracoContextDataToken, umbracoContext); - - return context; - } - - protected IUmbracoContext GetUmbracoContext(string url, int templateId, RouteData routeData = null, bool setSingleton = false) - { - var svcCtx = GetServiceContext(); - - var databaseFactory = TestObjects.GetDatabaseFactoryMock(); - - //var appCtx = new ApplicationContext( - // new DatabaseContext(databaseFactory, logger, Mock.Of(), Mock.Of()), - // svcCtx, - // CacheHelper.CreateDisabledCacheHelper(), - // new ProfilingLogger(logger, Mock.Of())) { /*IsReady = true*/ }; - - var cache = NoAppCache.Instance; - //var provider = new ScopeUnitOfWorkProvider(databaseFactory, new RepositoryFactory(Mock.Of())); - var scopeProvider = TestObjects.GetScopeProvider(NullLoggerFactory.Instance); - var factory = Mock.Of(); - var umbracoContextAccessor = Mock.Of(); - _service = new XmlPublishedSnapshotService(svcCtx, factory, scopeProvider, cache, - null, null, - umbracoContextAccessor, null, null, null, - new TestDefaultCultureAccessor(), - Current.LoggerFactory, TestObjects.GetGlobalSettings(), - TestHelper.GetHostingEnvironment(), - TestHelper.GetHostingEnvironmentLifetime(), - ShortStringHelper, - new SiteDomainHelper(), - Factory.GetInstance(), - null, true, false - ); // no events - - var http = GetHttpContextFactory(url, routeData).HttpContext; - - var httpContextAccessor = TestHelper.GetHttpContextAccessor(http); - var globalSettings = TestObjects.GetGlobalSettings(); - - var ctx = new UmbracoContext( - httpContextAccessor, - _service, - Mock.Of(), - globalSettings, - HostingEnvironment, - new TestVariationContextAccessor(), - UriUtility, - new AspNetCookieManager(httpContextAccessor)); - - //if (setSingleton) - //{ - // UmbracoContext.Current = ctx; - //} - - return ctx; - } - - protected FakeHttpContextFactory GetHttpContextFactory(string url, RouteData routeData = null) - { - var factory = routeData != null - ? new FakeHttpContextFactory(url, routeData) - : new FakeHttpContextFactory(url); - - return factory; - } - - #endregion - } -} From 35c22f90748067aa0e7886d3be077c85fc9e92f7 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 20 Oct 2020 21:30:45 +0200 Subject: [PATCH 4/4] Introduced SetViewDataAsync on UmbracoViewPage Signed-off-by: Bjarke Berg --- .../Views/UmbracoViewPageTests.cs | 378 +++++++++--------- .../AspNetCore/UmbracoViewPage.cs | 58 +++ 2 files changed, 243 insertions(+), 193 deletions(-) diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Views/UmbracoViewPageTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Views/UmbracoViewPageTests.cs index 2bc8a53cc1..3b52d0701e 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Views/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Views/UmbracoViewPageTests.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Mvc.ViewFeatures; using NUnit.Framework; using Umbraco.Core.Models.PublishedContent; using Umbraco.Web.Common.AspNetCore; +using Umbraco.Web.Common.ModelBinders; using Umbraco.Web.Models; namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.Views @@ -49,19 +50,16 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.Views Assert.IsInstanceOf(view.Model); } - // [Test] - // public void RenderModel_ContentType1_To_ContentType2() - // { - // // everything is strongly typed, so I'm not even allowed to try and set the ViewData with the wrong type - // var content = new ContentType1(null); - // var model = new ContentModel(content); - // var view = new ContentType2TestPage(); - // var viewData = new ViewDataDictionary(model); - // - // view.ViewContext = GetViewContext(); - // - // Assert.Throws(() => view.SetViewDataX(viewData)); - // } + [Test] + public void RenderModel_ContentType1_To_ContentType2() + { + var content = new ContentType1(null); + var model = new ContentModel(content); + var view = new ContentType2TestPage(); + var viewData = GetViewDataDictionary(model); + + Assert.ThrowsAsync(async () => await view.SetViewDataAsyncX(viewData)); + } [Test] public void RenderModel_ContentType1_To_RenderModelOf_ContentType1() @@ -90,19 +88,16 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.Views Assert.IsInstanceOf(view.Model.Content); } - // [Test] - // public void RenderModel_ContentType1_To_RenderModelOf_ContentType2() - // { - // // everything is strongly typed, so I'm not even allowed to try and create the ContentModel with the wrong type - // var content = new ContentType1(null); - // var model = new ContentModel(content); - // var view = new RenderModelOfContentType2TestPage(); - // var viewData = new ViewDataDictionary(model); - // - // view.ViewContext = GetViewContext(); - // - // Assert.Throws(() => view.SetViewDataX(viewData)); - // } + [Test] + public void RenderModel_ContentType1_To_RenderModelOf_ContentType2() + { + var content = new ContentType1(null); + var model = new ContentModel(content); + var view = new RenderModelOfContentType2TestPage(); + var viewData = GetViewDataDictionary(model); + + Assert.ThrowsAsync(async () => await view.SetViewDataAsyncX(viewData)); + } #endregion @@ -121,189 +116,173 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.Views Assert.AreSame(model, view.Model); } - // [Test] - // public void RenderModelOf_ContentType1_To_ContentType1() - // { - // // Can't create viewdata with ContentType1 from ContentModel because it doesn't actually inherit ContentType1 - // // And can't set viewdata from ContentModel because the page expects ContentType1 and not ContentModel - // // And if I change it the test will be the same as RenderModel_ContentType1_To_ContentType1 - // var content = new ContentType1(null); - // var model = new ContentModel(content); - // var view = new ContentType1TestPage(); - // var viewData = GetViewDataDictionary>(model); - // - // view.ViewData = viewData; - // - // Assert.IsInstanceOf(view.Model); - // } + [Test] + public async Task RenderModelOf_ContentType1_To_ContentType1() + { + var content = new ContentType1(null); + var model = new ContentModel(content); + var view = new ContentType1TestPage(); + var viewData = GetViewDataDictionary>(model); - // [Test] - // public void RenderModelOf_ContentType2_To_ContentType1() - // { - // // Same issue as above test - // var content = new ContentType2(null); - // var model = new ContentModel(content); - // var view = new ContentType1TestPage(); - // var viewData = new ViewDataDictionary(model); - // - // view.ViewContext = GetViewContext(); - // view.SetViewDataX(viewData); - // - // Assert.IsInstanceOf(view.Model); - // } + await view.SetViewDataAsyncX(viewData); - // [Test] - // public void RenderModelOf_ContentType1_To_ContentType2() - // { - // // Same issue as above, and as RenderModel1_ContentType1_To_ContentType2 - // var content = new ContentType1(null); - // var model = new ContentModel(content); - // var view = new ContentType2TestPage(); - // var viewData = new ViewDataDictionary(model); - // - // view.ViewContext = GetViewContext(); - // Assert.Throws(() => view.SetViewDataX(viewData)); - // } + Assert.IsInstanceOf(view.Model); + } - // [Test] - // public void RenderModelOf_ContentType1_To_RenderModelOf_ContentType1() - // { - // // It's the same as RenderModel_ContentType1_To_RenderModelOf_ContentType1 - // var content = new ContentType1(null); - // var model = new ContentModel(content); - // var view = new RenderModelOfContentType1TestPage(); - // var viewData = GetViewDataDictionary>(model); - // - // view.ViewData = viewData; - // - // Assert.IsInstanceOf>(view.Model); - // Assert.IsInstanceOf(view.Model.Content); - // } + [Test] + public async Task RenderModelOf_ContentType2_To_ContentType1() + { + var content = new ContentType2(null); + var model = new ContentModel(content); + var view = new ContentType1TestPage(); + var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary()) + { + Model = model + }; - // [Test] - // public void RenderModelOf_ContentType2_To_RenderModelOf_ContentType1() - // { - // // Same as RenderModel_ContentType2_To_RenderModelOf_ContentType1 after merge - // var content = new ContentType2(null); - // var model = new ContentModel(content); - // var view = new RenderModelOfContentType1TestPage(); - // var viewData = GetViewDataDictionary>(model); - // - // view.ViewData = viewData; - // - // Assert.IsInstanceOf>(view.Model); - // Assert.IsInstanceOf(view.Model.Content); - // } + await view.SetViewDataAsyncX(viewData); - // [Test] - // public void RenderModelOf_ContentType1_To_RenderModelOf_ContentType2() - // { - // // Same issue as RenderModel1_ContentType1_To_ContentType2 - // var content = new ContentType1(null); - // var model = new ContentModel(content); - // var view = new RenderModelOfContentType2TestPage(); - // var viewData = new ViewDataDictionary(model); - // - // view.ViewContext = GetViewContext(); - // Assert.Throws(() => view.SetViewDataX(viewData)); - // } + Assert.IsInstanceOf(view.Model); + } + + [Test] + public async Task RenderModelOf_ContentType1_To_ContentType2() + { + + var content = new ContentType1(null); + var model = new ContentModel(content); + var view = new ContentType2TestPage(); + var viewData = GetViewDataDictionary(model); + + Assert.ThrowsAsync(async () => await view.SetViewDataAsyncX(viewData)); + } + + [Test] + public void RenderModelOf_ContentType1_To_RenderModelOf_ContentType1() + { + var content = new ContentType1(null); + var model = new ContentModel(content); + var view = new RenderModelOfContentType1TestPage(); + var viewData = GetViewDataDictionary>(model); + + view.ViewData = viewData; + + Assert.IsInstanceOf>(view.Model); + Assert.IsInstanceOf(view.Model.Content); + } + + [Test] + public async Task RenderModelOf_ContentType2_To_RenderModelOf_ContentType1() + { + var content = new ContentType2(null); + var model = new ContentModel(content); + var view = new RenderModelOfContentType1TestPage(); + var viewData = GetViewDataDictionary>(model); + + await view.SetViewDataAsyncX(viewData); + + Assert.IsInstanceOf>(view.Model); + Assert.IsInstanceOf(view.Model.Content); + } + + [Test] + public void RenderModelOf_ContentType1_To_RenderModelOf_ContentType2() + { + var content = new ContentType1(null); + var model = new ContentModel(content); + var view = new RenderModelOfContentType2TestPage(); + var viewData = GetViewDataDictionary(model); + + Assert.ThrowsAsync(async () => await view.SetViewDataAsyncX(viewData)); + } #endregion #region ContentType To ... - // [Test] - // public void ContentType1_To_RenderModel() - // { - // // ContentType1 cannot be sat as ViewData because ContentModel is expected - // var content = new ContentType1(null); - // var view = new RenderModelTestPage(); - // - // var viewData = GetViewDataDictionary(content); - // - // view.ViewData = viewData; - // - // Assert.IsInstanceOf(view.Model); - // } + [Test] + public async Task ContentType1_To_RenderModel() + { + var content = new ContentType1(null); + var view = new RenderModelTestPage(); - // [Test] - // public void ContentType1_To_RenderModelOf_ContentType1() - // { - // // same as above but with ContentModel instead of ContentModel - // var content = new ContentType1(null); - // var view = new RenderModelOfContentType1TestPage(); - // - // var viewData = GetViewDataDictionary(content); - // view.ViewData = viewData; - // - // Assert.IsInstanceOf>(view.Model); - // Assert.IsInstanceOf(view.Model.Content); - // } + var viewData = GetViewDataDictionary(content); - // [Test] - // public void ContentType2_To_RenderModelOf_ContentType1() - // { - // // Same as above but with ContentModel - // var content = new ContentType2(null); - // var view = new RenderModelOfContentType1TestPage(); - // var viewData = GetViewDataDictionary(content); - // - // view.ViewData = viewData; - // - // Assert.IsInstanceOf>(view.Model); - // Assert.IsInstanceOf(view.Model.Content); - // } + await view.SetViewDataAsyncX(viewData); - // [Test] - // public void ContentType1_To_RenderModelOf_ContentType2() - // { - // // Same as above, and as RenderModel1_ContentType1_To_ContentType2 - // var content = new ContentType1(null); - // var view = new RenderModelOfContentType2TestPage(); - // var viewData = new ViewDataDictionary(content); - // - // view.ViewContext = GetViewContext(); - // Assert.Throws(() =>view.SetViewDataX(viewData)); - // } + Assert.IsInstanceOf(view.Model); + } - // [Test] - // public void ContentType1_To_ContentType1() - // { - // // Same as ContentType1_To_ContentType1 - // var content = new ContentType1(null); - // var view = new ContentType1TestPage(); - // var viewdata = GetViewDataDictionary(content); - // - // view.ViewData = viewdata; - // - // Assert.IsInstanceOf(view.Model); - // } + [Test] + public async Task ContentType1_To_RenderModelOf_ContentType1() + { + var content = new ContentType1(null); + var view = new RenderModelOfContentType1TestPage(); - // [Test] - // public void ContentType1_To_ContentType2() - // { - // // Same issue as RenderModel1_ContentType1_To_ContentType2 - // var content = new ContentType1(null); - // var view = new ContentType2TestPage(); - // var viewData = new ViewDataDictionary(content); - // - // view.ViewContext = GetViewContext(); - // Assert.Throws(() => view.SetViewDataX(viewData)); - // } + var viewData = GetViewDataDictionary(content); + await view.SetViewDataAsyncX(viewData); - // [Test] - // public void ContentType2_To_ContentType1() - // { - // // Will be the same as RenderModel_ContentType2_To_ContentType1 after merge - // var content = new ContentType2(null); - // var view = new ContentType1TestPage(); - // var viewData = new ViewDataDictionary(content); - // - // view.ViewContext = GetViewContext(); - // view.SetViewDataX(viewData); - // - // Assert.IsInstanceOf(view.Model); - // } + Assert.IsInstanceOf>(view.Model); + Assert.IsInstanceOf(view.Model.Content); + } + + [Test] + public async Task ContentType2_To_RenderModelOf_ContentType1() + { + // Same as above but with ContentModel + var content = new ContentType2(null); + var view = new RenderModelOfContentType1TestPage(); + var viewData = GetViewDataDictionary(content); + + await view.SetViewDataAsyncX(viewData); + + Assert.IsInstanceOf>(view.Model); + Assert.IsInstanceOf(view.Model.Content); + } + + [Test] + public void ContentType1_To_RenderModelOf_ContentType2() + { + var content = new ContentType1(null); + var view = new RenderModelOfContentType2TestPage(); + var viewData = GetViewDataDictionary(content); + + Assert.ThrowsAsync(async () => await view.SetViewDataAsyncX(viewData)); + } + + [Test] + public async Task ContentType1_To_ContentType1() + { + var content = new ContentType1(null); + var view = new ContentType1TestPage(); + var viewData = GetViewDataDictionary(content); + + await view.SetViewDataAsyncX(viewData); + + Assert.IsInstanceOf(view.Model); + } + + [Test] + public void ContentType1_To_ContentType2() + { + var content = new ContentType1(null); + var view = new ContentType2TestPage(); + var viewData = GetViewDataDictionary(content); + + Assert.ThrowsAsync(async () => await view.SetViewDataAsyncX(viewData)); + } + + [Test] + public async Task ContentType2_To_ContentType1() + { + var content = new ContentType2(null); + var view = new ContentType1TestPage(); + var viewData = GetViewDataDictionary(content); + + await view.SetViewDataAsyncX(viewData); + + Assert.IsInstanceOf(view.Model); + } #endregion @@ -315,6 +294,14 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.Views return new ViewDataDictionary(sourceViewDataDictionary, model); } + private ViewDataDictionary GetViewDataDictionary(object model) + { + var sourceViewDataDictionary = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary()); + return new ViewDataDictionary(sourceViewDataDictionary) + { + Model = model + }; + } #endregion @@ -336,6 +323,11 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.Views { throw new NotImplementedException(); } + + public async Task SetViewDataAsyncX(ViewDataDictionary viewData) + { + await SetViewDataAsync(viewData); + } } public class RenderModelTestPage : TestPage diff --git a/src/Umbraco.Web.Common/AspNetCore/UmbracoViewPage.cs b/src/Umbraco.Web.Common/AspNetCore/UmbracoViewPage.cs index fde3d095fe..cf41670d8e 100644 --- a/src/Umbraco.Web.Common/AspNetCore/UmbracoViewPage.cs +++ b/src/Umbraco.Web.Common/AspNetCore/UmbracoViewPage.cs @@ -1,7 +1,10 @@ using System; using System.Text; +using System.Threading.Tasks; using Microsoft.AspNetCore.Http.Extensions; +using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Razor; +using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Umbraco.Core; @@ -11,6 +14,7 @@ using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Strings; +using Umbraco.Web.Common.ModelBinders; namespace Umbraco.Web.Common.AspNetCore { @@ -29,6 +33,7 @@ namespace Umbraco.Web.Common.AspNetCore private ContentSettings ContentSettings => Context.RequestServices.GetRequiredService>().Value; private IProfilerHtml ProfilerHtml => Context.RequestServices.GetRequiredService(); private IIOHelper IOHelper => Context.RequestServices.GetRequiredService(); + private ContentModelBinder ContentModelBinder => new ContentModelBinder(); protected IUmbracoContext UmbracoContext => _umbracoContext ??= UmbracoContextAccessor.UmbracoContext; @@ -85,5 +90,58 @@ namespace Umbraco.Web.Common.AspNetCore base.WriteLiteral(value); } + + // maps model + protected async Task SetViewDataAsync(ViewDataDictionary viewData) + { + // capture the model before we tinker with the viewData + var viewDataModel = viewData.Model; + + // map the view data (may change its type, may set model to null) + viewData = MapViewDataDictionary(viewData, typeof (TModel)); + + // bind the model + var bindingContext = new DefaultModelBindingContext(); + await ContentModelBinder.BindModelAsync(bindingContext, viewDataModel, typeof (TModel)); + + viewData.Model = bindingContext.Result.Model; + + // set the view data + ViewData = (ViewDataDictionary) viewData; + } + + // viewData is the ViewDataDictionary (maybe ) that we have + // modelType is the type of the model that we need to bind to + // + // figure out whether viewData can accept modelType else replace it + // + private static ViewDataDictionary MapViewDataDictionary(ViewDataDictionary viewData, Type modelType) + { + var viewDataType = viewData.GetType(); + + + if (viewDataType.IsGenericType) + { + // ensure it is the proper generic type + var def = viewDataType.GetGenericTypeDefinition(); + if (def != typeof(ViewDataDictionary<>)) + throw new Exception("Could not map viewData of type \"" + viewDataType.FullName + "\"."); + + // get the viewData model type and compare with the actual view model type: + // viewData is ViewDataDictionary and we will want to assign an + // object of type modelType to the Model property of type viewDataModelType, we + // need to check whether that is possible + var viewDataModelType = viewDataType.GenericTypeArguments[0]; + + if (viewDataModelType.IsAssignableFrom(modelType)) + return viewData; + } + + // if not possible or it is not generic then we need to create a new ViewDataDictionary + var nViewDataType = typeof(ViewDataDictionary<>).MakeGenericType(modelType); + var tViewData = new ViewDataDictionary(viewData) { Model = null }; // temp view data to copy values + var nViewData = (ViewDataDictionary)Activator.CreateInstance(nViewDataType, tViewData); + return nViewData; + } } }