From 1c74e317154dc540882dad014dac01f0d350eee6 Mon Sep 17 00:00:00 2001 From: Ryan Brandenburg Date: Thu, 9 Mar 2017 16:19:20 -0800 Subject: [PATCH] Add tests for PageActionInvokerProvider (#5882) --- .../Infrastructure/IPageLoader.cs | 2 +- .../Internal/DefaultPageLoader.cs | 29 +- .../Internal/PageActionInvokerProvider.cs | 50 +-- .../RazorPagesTest.cs | 27 +- .../Internal/PageActionInvokerProviderTest.cs | 341 +++++++++++++++++- .../HelloWorldWithPageModelHandler.cs | 5 + .../HelloWorldWithPageModelHandler.cshtml | 7 +- 7 files changed, 413 insertions(+), 48 deletions(-) diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/IPageLoader.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/IPageLoader.cs index 09b61fe86a..68d69634c2 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/IPageLoader.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/IPageLoader.cs @@ -4,6 +4,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure { public interface IPageLoader { - Type Load(PageActionDescriptor actionDescriptor); + CompiledPageActionDescriptor Load(PageActionDescriptor actionDescriptor); } } diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/DefaultPageLoader.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/DefaultPageLoader.cs index a15d24cfcc..e50be6ad58 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/DefaultPageLoader.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/DefaultPageLoader.cs @@ -4,8 +4,8 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.IO; using System.Linq; +using System.Reflection; using Microsoft.AspNetCore.Mvc.Razor.Compilation; using Microsoft.AspNetCore.Mvc.Razor.Internal; using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; @@ -16,6 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal { public class DefaultPageLoader : IPageLoader { + private const string ModelPropertyName = "Model"; private readonly RazorCompilationService _razorCompilationService; private readonly ICompilationService _compilationService; private readonly RazorProject _project; @@ -33,22 +34,20 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal _logger = logger; } - public Type Load(PageActionDescriptor actionDescriptor) + public CompiledPageActionDescriptor Load(PageActionDescriptor actionDescriptor) { var item = _project.GetItem(actionDescriptor.RelativePath); if (!item.Exists) { throw new InvalidOperationException($"File {actionDescriptor.RelativePath} was not found."); } - - RazorCodeDocument codeDocument; - RazorCSharpDocument cSharpDocument; + _logger.RazorFileToCodeCompilationStart(item.Path); var startTimestamp = _logger.IsEnabled(LogLevel.Debug) ? Stopwatch.GetTimestamp() : 0; - codeDocument = CreateCodeDocument(item); - cSharpDocument = _razorCompilationService.ProcessCodeDocument(codeDocument); + var codeDocument = CreateCodeDocument(item); + var cSharpDocument = _razorCompilationService.ProcessCodeDocument(codeDocument); _logger.RazorFileToCodeCompilationEnd(item.Path, startTimestamp); @@ -63,7 +62,21 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal } compilationResult.EnsureSuccessful(); - return compilationResult.CompiledType; + + // If a model type wasn't set in code then the model property's type will be the same + // as the compiled type. + var pageType = compilationResult.CompiledType.GetTypeInfo(); + var modelType = pageType.GetProperty(ModelPropertyName)?.PropertyType.GetTypeInfo(); + if (modelType == pageType) + { + modelType = null; + } + + return new CompiledPageActionDescriptor(actionDescriptor) + { + ModelTypeInfo = modelType, + PageTypeInfo = pageType, + }; } private RazorCodeDocument CreateCodeDocument(RazorProjectItem item) diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvokerProvider.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvokerProvider.cs index 47ec269dda..325d1ab721 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvokerProvider.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvokerProvider.cs @@ -23,8 +23,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal { public class PageActionInvokerProvider : IActionInvokerProvider { + private static readonly string[] _handlerMethodNames = new string[] { "OnGet", "OnPost" }; private const string PageStartFileName = "_PageStart.cshtml"; - private const string ModelPropertyName = "Model"; private readonly IPageLoader _loader; private readonly IPageFactoryProvider _pageFactoryProvider; private readonly IPageModelFactoryProvider _modelFactoryProvider; @@ -166,34 +166,20 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal FilterItem[] cachedFilters) { var actionDescriptor = (PageActionDescriptor)context.ActionContext.ActionDescriptor; - var compiledType = _loader.Load(actionDescriptor).GetTypeInfo(); - - // If a model type wasn't set in code then the model property's type will be the same - // as the compiled type. - var modelType = compiledType.GetProperty(ModelPropertyName)?.PropertyType.GetTypeInfo(); - if (modelType == compiledType) - { - modelType = null; - } - - var compiledActionDescriptor = new CompiledPageActionDescriptor(actionDescriptor) - { - ModelTypeInfo = modelType, - PageTypeInfo = compiledType, - }; + var compiledActionDescriptor = _loader.Load(actionDescriptor); var pageFactory = _pageFactoryProvider.CreatePageFactory(compiledActionDescriptor); var pageDisposer = _pageFactoryProvider.CreatePageDisposer(compiledActionDescriptor); Func modelFactory = null; Action modelReleaser = null; - if (modelType == null) + if (compiledActionDescriptor.ModelTypeInfo == null) { - PopulateHandlerMethodDescriptors(compiledType, compiledActionDescriptor); + PopulateHandlerMethodDescriptors(compiledActionDescriptor.PageTypeInfo, compiledActionDescriptor); } else { - PopulateHandlerMethodDescriptors(modelType, compiledActionDescriptor); + PopulateHandlerMethodDescriptors(compiledActionDescriptor.ModelTypeInfo, compiledActionDescriptor); modelFactory = _modelFactoryProvider.CreateModelFactory(compiledActionDescriptor); modelReleaser = _modelFactoryProvider.CreateModelDisposer(compiledActionDescriptor); @@ -211,30 +197,34 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal cachedFilters); } - private List> GetPageStartFactories(CompiledPageActionDescriptor descriptor) + // Internal for testing. + internal List> GetPageStartFactories(CompiledPageActionDescriptor descriptor) { var pageStartFactories = new List>(); var pageStartItems = _razorProject.FindHierarchicalItems(descriptor.ViewEnginePath, PageStartFileName); foreach (var item in pageStartItems) { - var factoryResult = _razorPageFactoryProvider.CreateFactory(item.Path); - if (factoryResult.Success) + if(item.Exists) { - pageStartFactories.Insert(0, factoryResult.RazorPageFactory); + var factoryResult = _razorPageFactoryProvider.CreateFactory(item.Path); + if (factoryResult.Success) + { + pageStartFactories.Insert(0, factoryResult.RazorPageFactory); + } } } return pageStartFactories; } - private static void PopulateHandlerMethodDescriptors(TypeInfo type, CompiledPageActionDescriptor actionDescriptor) + // Internal for testing. + internal static void PopulateHandlerMethodDescriptors(TypeInfo type, CompiledPageActionDescriptor actionDescriptor) { - var methods = type.GetMethods(); - for (var i = 0; i < methods.Length; i++) + for (var i = 0; i < _handlerMethodNames.Length; i++) { - var method = methods[i]; - if (method.Name.StartsWith("OnGet", StringComparison.Ordinal) || - method.Name.StartsWith("OnPost", StringComparison.Ordinal)) + var methodName = _handlerMethodNames[i]; + var method = type.GetMethod(methodName); + if (method != null && !method.IsGenericMethod) { actionDescriptor.HandlerMethods.Add(new HandlerMethodDescriptor() { @@ -245,7 +235,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal } } - private class InnerCache + internal class InnerCache { public InnerCache(int version) { diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs index 7c5b18fc62..48bdfa6729 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs @@ -95,6 +95,31 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests Assert.Equal("Hello, handler!", content.Trim()); } + [Fact] + public async Task HelloWorldWithPageModelHandler_CanPostContent() + { + // Arrange + var getRequest = new HttpRequestMessage(HttpMethod.Get, "http://localhost/HelloWorldWithPageModelHandler?message=message"); + var getResponse = await Client.SendAsync(getRequest); + var getResponseBody = await getResponse.Content.ReadAsStringAsync(); + var formToken = AntiforgeryTestHelper.RetrieveAntiforgeryToken(getResponseBody, "/HelloWorlWithPageModelHandler"); + var cookie = AntiforgeryTestHelper.RetrieveAntiforgeryCookie(getResponse); + + + var postRequest = new HttpRequestMessage(HttpMethod.Post, "http://localhost/HelloWorldWithPageModelHandler"); + postRequest.Headers.Add("Cookie", cookie.Key + "=" + cookie.Value); + postRequest.Headers.Add("RequestVerificationToken", formToken); + + // Act + var response = await Client.SendAsync(postRequest); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + var content = await response.Content.ReadAsStringAsync(); + Assert.StartsWith("Hello, You posted!", content.Trim()); + } + [Fact] public async Task HelloWorldWithPageModelHandler_CanGetContent() { @@ -108,7 +133,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests Assert.Equal(HttpStatusCode.OK, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); - Assert.Equal("Hello, pagemodel!", content.Trim()); + Assert.StartsWith("Hello, pagemodel!", content.Trim()); } [Fact] diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerProviderTest.cs index 3254226c5e..4ca9a13b62 100644 --- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerProviderTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerProviderTest.cs @@ -2,7 +2,9 @@ // 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.Diagnostics; +using System.Reflection; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Filters; @@ -14,6 +16,7 @@ using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Razor.Evolution; using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging.Testing; using Microsoft.Extensions.Primitives; using Moq; @@ -37,7 +40,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal var loader = new Mock(); loader.Setup(l => l.Load(It.IsAny())) - .Returns(typeof(object)); + .Returns(CreateCompiledPageActionDescriptor(descriptor)); var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1); var actionDescriptorProvider = new Mock(); actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection); @@ -69,6 +72,61 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal Assert.Null(entry.ReleaseModel); } + [Fact] + public void OnProvidersExecuting_SetsHandlers() + { + // Arrange + var descriptor = new PageActionDescriptor + { + RelativePath = "Path1", + FilterDescriptors = new FilterDescriptor[0], + }; + Func factory = _ => null; + Action releaser = (_, __) => { }; + + var loader = new Mock(); + loader.Setup(l => l.Load(It.IsAny())) + .Returns(CreateCompiledPageActionDescriptor(descriptor, typeof(TestSetPageWithModel))); + var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1); + var actionDescriptorProvider = new Mock(); + actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection); + var pageFactoryProvider = new Mock(); + pageFactoryProvider.Setup(f => f.CreatePageFactory(It.IsAny())) + .Returns(factory); + pageFactoryProvider.Setup(f => f.CreatePageDisposer(It.IsAny())) + .Returns(releaser); + + var modelFactoryProvider = new Mock(); + + var invokerProvider = CreateInvokerProvider( + loader.Object, + actionDescriptorProvider.Object, + pageFactoryProvider.Object, + modelFactoryProvider.Object); + var context = new ActionInvokerProviderContext( + new ActionContext(new DefaultHttpContext(), new RouteData(), descriptor)); + + // Act + invokerProvider.OnProvidersExecuting(context); + + // Assert + Assert.NotNull(context.Result); + var actionInvoker = Assert.IsType(context.Result); + var entry = actionInvoker.CacheEntry; + + Assert.Collection(entry.ActionDescriptor.HandlerMethods, + handlerDescriptor => + { + Assert.Equal(nameof(TestSetPageModel.OnGet), handlerDescriptor.Method.Name); + Assert.NotNull(handlerDescriptor.Executor); + }, + handlerDescriptor => + { + Assert.Equal(nameof(TestSetPageModel.OnPost), handlerDescriptor.Method.Name); + Assert.NotNull(handlerDescriptor.Executor); + }); + } + [Fact] public void OnProvidersExecuting_WithModel_PopulatesCacheEntry() { @@ -85,7 +143,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal var loader = new Mock(); loader.Setup(l => l.Load(It.IsAny())) - .Returns(typeof(PageWithModel)); + .Returns(CreateCompiledPageActionDescriptor(descriptor, pageType: typeof(PageWithModel))); var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1); var actionDescriptorProvider = new Mock(); actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection); @@ -137,7 +195,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal var loader = new Mock(); loader.Setup(l => l.Load(It.IsAny())) - .Returns(typeof(PageWithModel)); + .Returns(CreateCompiledPageActionDescriptor(descriptor, pageType: typeof(PageWithModel))); var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1); var actionDescriptorProvider = new Mock(); actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection); @@ -184,7 +242,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal var loader = new Mock(); loader.Setup(l => l.Load(It.IsAny())) - .Returns(typeof(PageWithModel)); + .Returns(CreateCompiledPageActionDescriptor(descriptor, pageType: typeof(PageWithModel))); var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1); var actionDescriptorProvider = new Mock(); actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection); @@ -215,7 +273,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal }); } - [Fact] public void OnProvidersExecuting_CachesEntries() { @@ -227,7 +284,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal }; var loader = new Mock(); loader.Setup(l => l.Load(It.IsAny())) - .Returns(typeof(object)); + .Returns(CreateCompiledPageActionDescriptor(descriptor)); var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1); var actionDescriptorProvider = new Mock(); actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection); @@ -274,7 +331,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal var loader = new Mock(); loader.Setup(l => l.Load(It.IsAny())) - .Returns(typeof(object)); + .Returns(CreateCompiledPageActionDescriptor(descriptor)); var invokerProvider = CreateInvokerProvider( loader.Object, actionDescriptorProvider.Object); @@ -299,6 +356,229 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal Assert.NotSame(entry1, entry2); } + [Fact] + public void PopulateHandlerMethodDescriptors_DiscoversHandlersFromBaseType() + { + // Arrange + var descriptor = new PageActionDescriptor() + { + RelativePath = "Path1", + FilterDescriptors = new FilterDescriptor[0], + ViewEnginePath = "/Views/Deeper/Index.cshtml" + }; + + var actionDescriptor = CreateCompiledPageActionDescriptor(descriptor, typeof(InheritsMethods)); + + var type = actionDescriptor.ModelTypeInfo ?? actionDescriptor.PageTypeInfo; + + // Act + PageActionInvokerProvider.PopulateHandlerMethodDescriptors(type, actionDescriptor); + + // Assert + Assert.Collection(actionDescriptor.HandlerMethods, + (handler) => + { + Assert.Equal("OnGet", handler.Method.Name); + Assert.Equal(typeof(InheritsMethods), handler.Method.DeclaringType); + }, + (handler) => + { + Assert.Equal("OnPost", handler.Method.Name); + Assert.Equal(typeof(TestSetPageModel), handler.Method.DeclaringType); + }); + } + + [Fact] + public void PopulateHandlerMethodDescriptors_ProtectedMethodsNotFound() + { + // Arrange + var descriptor = new PageActionDescriptor() + { + RelativePath = "Path1", + FilterDescriptors = new FilterDescriptor[0], + ViewEnginePath = "/Views/Deeper/Index.cshtml" + }; + + var actionDescriptor = CreateCompiledPageActionDescriptor(descriptor, typeof(ProtectedModel)); + + var type = actionDescriptor.ModelTypeInfo ?? actionDescriptor.PageTypeInfo; + + // Act + PageActionInvokerProvider.PopulateHandlerMethodDescriptors(type, actionDescriptor); + + // Assert + Assert.Empty(actionDescriptor.HandlerMethods); + } + + [Fact] + public void PopulateHandlerMethodDescriptors_IgnoreGenericTypeParameters() + { + // Arrange + var descriptor = new PageActionDescriptor() + { + RelativePath = "Path1", + FilterDescriptors = new FilterDescriptor[0], + ViewEnginePath = "/Views/Deeper/Index.cshtml" + }; + + var actionDescriptor = CreateCompiledPageActionDescriptor(descriptor, typeof(GenericClassModel)); + + var type = actionDescriptor.ModelTypeInfo ?? actionDescriptor.PageTypeInfo; + + // Act + PageActionInvokerProvider.PopulateHandlerMethodDescriptors(type, actionDescriptor); + + // Assert + Assert.Empty(actionDescriptor.HandlerMethods); + } + + [Fact] + public void PopulateHandlerMethodDescriptors_AllowOnlyOneMethod() + { + // Arrange + var descriptor = new PageActionDescriptor() + { + RelativePath = "Path1", + FilterDescriptors = new FilterDescriptor[0], + ViewEnginePath = "/Views/Deeper/Index.cshtml" + }; + + var actionDescriptor = CreateCompiledPageActionDescriptor(descriptor, typeof(TestPageModel)); + + var type = actionDescriptor.ModelTypeInfo ?? actionDescriptor.PageTypeInfo; + + // Act + PageActionInvokerProvider.PopulateHandlerMethodDescriptors(type, actionDescriptor); + + // Assert + var handler = Assert.Single(actionDescriptor.HandlerMethods); + Assert.Equal("OnGet", handler.Method.Name); + } + + [Fact] + public void GetPageStartFactories_FindsFullHeirarchy() + { + // Arrange + var descriptor = new PageActionDescriptor() + { + RelativePath = "Path1", + FilterDescriptors = new FilterDescriptor[0], + ViewEnginePath = "/Views/Deeper/Index.cshtml" + }; + var loader = new Mock(); + loader.Setup(l => l.Load(It.IsAny())) + .Returns(CreateCompiledPageActionDescriptor(descriptor, typeof(TestPageModel))); + var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1); + var actionDescriptorProvider = new Mock(); + actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection); + + var fileProvider = new TestFileProvider(); + fileProvider.AddFile("/View/Deeper/Not_PageStart.cshtml", "page content"); + fileProvider.AddFile("/View/Wrong/_PageStart.cshtml", "page content"); + fileProvider.AddFile("/_PageStart.cshtml", "page content "); + fileProvider.AddFile("/Views/_PageStart.cshtml", "@page starts!"); + fileProvider.AddFile("/Views/Deeper/_PageStart.cshtml", "page content"); + + var razorProject = CreateRazorProject(fileProvider); + + var mock = new Mock(); + mock.Setup(p => p.CreateFactory("/Views/Deeper/_PageStart.cshtml")) + .Returns(new RazorPageFactoryResult(() => null, new List())) + .Verifiable(); + mock.Setup(p => p.CreateFactory("/Views/_PageStart.cshtml")) + .Returns(new RazorPageFactoryResult(() => null, new List())) + .Verifiable(); + mock.Setup(p => p.CreateFactory("/_PageStart.cshtml")) + .Returns(new RazorPageFactoryResult(() => null, new List())) + .Verifiable(); + var razorPageFactoryProvider = mock.Object; + + var invokerProvider = CreateInvokerProvider( + loader.Object, + actionDescriptorProvider.Object, + pageProvider: null, + modelProvider: null, + razorPageFactoryProvider: razorPageFactoryProvider, + razorProject: razorProject); + + var compiledDescriptor = CreateCompiledPageActionDescriptor(descriptor); + + // Act + var factories = invokerProvider.GetPageStartFactories(compiledDescriptor); + + // Assert + mock.Verify(); + } + + [Fact] + public void GetPageStartFactories_NoFactoriesForMissingFiles() + { + // Arrange + var descriptor = new PageActionDescriptor() + { + RelativePath = "Path1", + FilterDescriptors = new FilterDescriptor[0], + ViewEnginePath = "/Views/Deeper/Index.cshtml" + }; + var loader = new Mock(); + loader.Setup(l => l.Load(It.IsAny())) + .Returns(CreateCompiledPageActionDescriptor(descriptor, typeof(TestPageModel))); + var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1); + var actionDescriptorProvider = new Mock(); + actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection); + + // No files + var fileProvider = new TestFileProvider(); + + var razorProject = CreateRazorProject(fileProvider); + + var invokerProvider = CreateInvokerProvider( + loader.Object, + actionDescriptorProvider.Object, + pageProvider: null, + modelProvider: null, + razorPageFactoryProvider: CreateRazorPageFactoryProvider(), + razorProject: razorProject); + + var compiledDescriptor = CreateCompiledPageActionDescriptor(descriptor); + + // Act + var factories = invokerProvider.GetPageStartFactories(compiledDescriptor); + + // Assert + Assert.Empty(factories); + } + + private IRazorPageFactoryProvider CreateRazorPageFactoryProvider() + { + var mock = new Mock(); + mock.Setup(p => p.CreateFactory(It.IsAny())) + .Returns(new RazorPageFactoryResult(() => null, new List())); + return mock.Object; + } + + private RazorProject CreateRazorProject(IFileProvider fileProvider) + { + return new DefaultRazorProject(fileProvider); + } + + private static CompiledPageActionDescriptor CreateCompiledPageActionDescriptor( + PageActionDescriptor descriptor, + Type pageType = null) + { + TypeInfo modelTypeInfo = null; + if (pageType != null) + { + modelTypeInfo = pageType.GetTypeInfo().GetProperty("Model")?.PropertyType.GetTypeInfo(); + } + + return new CompiledPageActionDescriptor(descriptor) + { + ModelTypeInfo = modelTypeInfo, + PageTypeInfo = (pageType ?? typeof(object)).GetTypeInfo() + }; + } + private static PageActionInvokerProvider CreateInvokerProvider( IPageLoader loader, IActionDescriptorCollectionProvider actionDescriptorProvider, @@ -334,6 +614,53 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal NullLoggerFactory.Instance); } + private class GenericClassModel + { + public void OnGet() + { + + } + } + + private class TestSetPageWithModel + { + public TestSetPageModel Model { get; set; } + } + + private class InheritsMethods : TestSetPageModel + { + public new void OnGet() + { + + } + } + + private class ProtectedModel + { + protected void OnGet() + { + + } + + private void OnPost() + { + + } + } + + private class TestSetPageModel + { + public void OnGet() + { + + } + + public void OnPost() + { + + } + } + private class PageWithModel { public TestPageModel Model { get; set; } diff --git a/test/WebSites/RazorPagesWebSite/HelloWorldWithPageModelHandler.cs b/test/WebSites/RazorPagesWebSite/HelloWorldWithPageModelHandler.cs index 8706216524..d6622314da 100644 --- a/test/WebSites/RazorPagesWebSite/HelloWorldWithPageModelHandler.cs +++ b/test/WebSites/RazorPagesWebSite/HelloWorldWithPageModelHandler.cs @@ -13,5 +13,10 @@ namespace RazorPagesWebSite { Message = message; } + + public void OnPost() + { + Message = "You posted!"; + } } } diff --git a/test/WebSites/RazorPagesWebSite/HelloWorldWithPageModelHandler.cshtml b/test/WebSites/RazorPagesWebSite/HelloWorldWithPageModelHandler.cshtml index 707e27e253..cc94947637 100644 --- a/test/WebSites/RazorPagesWebSite/HelloWorldWithPageModelHandler.cshtml +++ b/test/WebSites/RazorPagesWebSite/HelloWorldWithPageModelHandler.cshtml @@ -1,4 +1,9 @@ @page @model RazorPagesWebSite.HelloWorldWithPageModelHandler -Hello, @Model.Message! \ No newline at end of file +Hello, @Model.Message! + +@using (Html.BeginForm()) +{ + @Html.AntiForgeryToken() +} \ No newline at end of file