parent
b8ab5c5063
commit
e28adbfb3d
|
|
@ -39,6 +39,7 @@
|
||||||
<Content Include="Views\Shared\HelloWorldPartial.cshtml" />
|
<Content Include="Views\Shared\HelloWorldPartial.cshtml" />
|
||||||
<Content Include="Views\Shared\MyView.cshtml" />
|
<Content Include="Views\Shared\MyView.cshtml" />
|
||||||
<Content Include="Views\Shared\_Layout.cshtml" />
|
<Content Include="Views\Shared\_Layout.cshtml" />
|
||||||
|
<Content Include="Views\_ViewStart.cshtml" />
|
||||||
<Content Include="web.config" />
|
<Content Include="web.config" />
|
||||||
<Content Include="web.Debug.config" />
|
<Content Include="web.Debug.config" />
|
||||||
<Content Include="web.Release.config" />
|
<Content Include="web.Release.config" />
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
@using MvcSample.Web.Models
|
@using MvcSample.Web.Models
|
||||||
@model User
|
@model User
|
||||||
@{
|
@{
|
||||||
Layout = "/Views/Shared/_Layout.cshtml";
|
|
||||||
ViewBag.Title = "Home Page";
|
ViewBag.Title = "Home Page";
|
||||||
string nullValue = null;
|
string nullValue = null;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,12 +27,6 @@
|
||||||
<div class="container body-content">
|
<div class="container body-content">
|
||||||
@RenderBody()
|
@RenderBody()
|
||||||
<hr />
|
<hr />
|
||||||
<address>
|
|
||||||
@if (@Model != null)
|
|
||||||
{
|
|
||||||
@Model.Address
|
|
||||||
}
|
|
||||||
</address>
|
|
||||||
<footer>
|
<footer>
|
||||||
<p>© @DateTime.Now.Year - My ASP.NET Application</p>
|
<p>© @DateTime.Now.Year - My ASP.NET Application</p>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
@{
|
||||||
|
Layout = "/Views/Shared/_Layout.cshtml";
|
||||||
|
}
|
||||||
|
|
@ -16,6 +16,11 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ViewContext ViewContext { get; set; }
|
ViewContext ViewContext { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the path to the page.
|
||||||
|
/// </summary>
|
||||||
|
string Path { get; set; }
|
||||||
|
|
||||||
string BodyContent { get; set; }
|
string BodyContent { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a <see cref="IRazorPage"/> for the specified path.
|
/// Creates a <see cref="IRazorPage"/> for the specified path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="viewPath">The path to locate the RazorPage.</param>
|
/// <param name="path">The path to locate the page.</param>
|
||||||
/// <returns>The IRazorPage instance if it exists, null otherwise.</returns>
|
/// <returns>The IRazorPage instance if it exists, null otherwise.</returns>
|
||||||
IRazorPage CreateInstance(string viewPath);
|
IRazorPage CreateInstance(string path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines methods for locating ViewStart pages that are applicable to a page.
|
||||||
|
/// </summary>
|
||||||
|
public interface IViewStartProvider
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Given a view path, returns a sequence of ViewStart instances
|
||||||
|
/// that are applicable to the specified view.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path of the page to locate ViewStart files for.</param>
|
||||||
|
/// <returns>A sequence of <see cref="IRazorPage"/> that represent ViewStart.</returns>
|
||||||
|
IEnumerable<IRazorPage> GetViewStartPages(string path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -30,6 +30,9 @@
|
||||||
<Compile Include="Compilation\RoslynCompilationService.cs" />
|
<Compile Include="Compilation\RoslynCompilationService.cs" />
|
||||||
<Compile Include="Extensions\DictionaryExtensions.cs" />
|
<Compile Include="Extensions\DictionaryExtensions.cs" />
|
||||||
<Compile Include="IRazorPage.cs" />
|
<Compile Include="IRazorPage.cs" />
|
||||||
|
<Compile Include="IViewStartProvider.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="ViewStartProvider.cs" />
|
||||||
<Compile Include="VirtualPathRazorPageFactory.cs" />
|
<Compile Include="VirtualPathRazorPageFactory.cs" />
|
||||||
<Compile Include="HelperResult.cs" />
|
<Compile Include="HelperResult.cs" />
|
||||||
<Compile Include="IRazorPageActivator.cs" />
|
<Compile Include="IRazorPageActivator.cs" />
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
// 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.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
[assembly: InternalsVisibleTo("Microsoft.AspNet.Mvc.Razor.Test")]
|
||||||
|
|
@ -42,6 +42,9 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string Path { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ViewContext ViewContext { get; set; }
|
public ViewContext ViewContext { get; set; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Mvc.Rendering;
|
using Microsoft.AspNet.Mvc.Rendering;
|
||||||
|
|
@ -17,6 +18,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
{
|
{
|
||||||
private readonly IRazorPageFactory _pageFactory;
|
private readonly IRazorPageFactory _pageFactory;
|
||||||
private readonly IRazorPageActivator _pageActivator;
|
private readonly IRazorPageActivator _pageActivator;
|
||||||
|
private readonly IViewStartProvider _viewStartProvider;
|
||||||
private readonly IRazorPage _page;
|
private readonly IRazorPage _page;
|
||||||
private readonly bool _executeViewHierarchy;
|
private readonly bool _executeViewHierarchy;
|
||||||
|
|
||||||
|
|
@ -30,11 +32,13 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
/// view start and layout pages are executed as part of the executing the page.</param>
|
/// view start and layout pages are executed as part of the executing the page.</param>
|
||||||
public RazorView([NotNull] IRazorPageFactory pageFactory,
|
public RazorView([NotNull] IRazorPageFactory pageFactory,
|
||||||
[NotNull] IRazorPageActivator pageActivator,
|
[NotNull] IRazorPageActivator pageActivator,
|
||||||
|
[NotNull] IViewStartProvider viewStartProvider,
|
||||||
[NotNull] IRazorPage page,
|
[NotNull] IRazorPage page,
|
||||||
bool executeViewHierarchy)
|
bool executeViewHierarchy)
|
||||||
{
|
{
|
||||||
_pageFactory = pageFactory;
|
_pageFactory = pageFactory;
|
||||||
_pageActivator = pageActivator;
|
_pageActivator = pageActivator;
|
||||||
|
_viewStartProvider = viewStartProvider;
|
||||||
_page = page;
|
_page = page;
|
||||||
_executeViewHierarchy = executeViewHierarchy;
|
_executeViewHierarchy = executeViewHierarchy;
|
||||||
}
|
}
|
||||||
|
|
@ -44,7 +48,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
{
|
{
|
||||||
if (_executeViewHierarchy)
|
if (_executeViewHierarchy)
|
||||||
{
|
{
|
||||||
var bodyContent = await RenderPageAsync(_page, context);
|
var bodyContent = await RenderPageAsync(_page, context, executeViewStart: true);
|
||||||
await RenderLayoutAsync(context, bodyContent);
|
await RenderLayoutAsync(context, bodyContent);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -53,7 +57,9 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> RenderPageAsync(IRazorPage page, ViewContext context)
|
private async Task<string> RenderPageAsync(IRazorPage page,
|
||||||
|
ViewContext context,
|
||||||
|
bool executeViewStart)
|
||||||
{
|
{
|
||||||
var contentBuilder = new StringBuilder(1024);
|
var contentBuilder = new StringBuilder(1024);
|
||||||
using (var bodyWriter = new StringWriter(contentBuilder))
|
using (var bodyWriter = new StringWriter(contentBuilder))
|
||||||
|
|
@ -64,6 +70,11 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
context.Writer = bodyWriter;
|
context.Writer = bodyWriter;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (executeViewStart)
|
||||||
|
{
|
||||||
|
// Execute view starts using the same context + writer as the page to render.
|
||||||
|
await RenderViewStartAsync(context);
|
||||||
|
}
|
||||||
await RenderPageCoreAsync(page, context);
|
await RenderPageCoreAsync(page, context);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|
@ -86,6 +97,19 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
await page.ExecuteAsync();
|
await page.ExecuteAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task RenderViewStartAsync(ViewContext context)
|
||||||
|
{
|
||||||
|
var viewStarts = _viewStartProvider.GetViewStartPages(_page.Path);
|
||||||
|
|
||||||
|
foreach (var viewStart in viewStarts)
|
||||||
|
{
|
||||||
|
await RenderPageCoreAsync(viewStart, context);
|
||||||
|
|
||||||
|
// Copy over interesting properties from the ViewStart page to the entry page.
|
||||||
|
_page.Layout = viewStart.Layout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task RenderLayoutAsync(ViewContext context,
|
private async Task RenderLayoutAsync(ViewContext context,
|
||||||
string bodyContent)
|
string bodyContent)
|
||||||
{
|
{
|
||||||
|
|
@ -104,7 +128,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
layoutPage.PreviousSectionWriters = previousPage.SectionWriters;
|
layoutPage.PreviousSectionWriters = previousPage.SectionWriters;
|
||||||
layoutPage.BodyContent = bodyContent;
|
layoutPage.BodyContent = bodyContent;
|
||||||
|
|
||||||
bodyContent = await RenderPageAsync(layoutPage, context);
|
bodyContent = await RenderPageAsync(layoutPage, context, executeViewStart: false);
|
||||||
|
|
||||||
// Verify that RenderBody is called, or that RenderSection is called for all sections
|
// Verify that RenderBody is called, or that RenderSection is called for all sections
|
||||||
layoutPage.EnsureBodyAndSectionsWereRendered();
|
layoutPage.EnsureBodyAndSectionsWereRendered();
|
||||||
|
|
|
||||||
|
|
@ -28,12 +28,15 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
||||||
private readonly IRazorPageFactory _pageFactory;
|
private readonly IRazorPageFactory _pageFactory;
|
||||||
private readonly IRazorPageActivator _viewActivator;
|
private readonly IRazorPageActivator _viewActivator;
|
||||||
|
private readonly IViewStartProvider _viewStartProvider;
|
||||||
|
|
||||||
public RazorViewEngine(IRazorPageFactory pageFactory,
|
public RazorViewEngine(IRazorPageFactory pageFactory,
|
||||||
IRazorPageActivator viewActivator)
|
IRazorPageActivator viewActivator,
|
||||||
|
IViewStartProvider viewStartProvider)
|
||||||
{
|
{
|
||||||
_pageFactory = pageFactory;
|
_pageFactory = pageFactory;
|
||||||
_viewActivator = viewActivator;
|
_viewActivator = viewActivator;
|
||||||
|
_viewStartProvider = viewStartProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<string> ViewLocationFormats
|
public IEnumerable<string> ViewLocationFormats
|
||||||
|
|
@ -96,6 +99,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
{
|
{
|
||||||
var view = new RazorView(_pageFactory,
|
var view = new RazorView(_pageFactory,
|
||||||
_viewActivator,
|
_viewActivator,
|
||||||
|
_viewStartProvider,
|
||||||
page,
|
page,
|
||||||
executeViewHierarchy: !partial);
|
executeViewHierarchy: !partial);
|
||||||
return ViewEngineResult.Found(viewName, view);
|
return ViewEngineResult.Found(viewName, view);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.Framework.Runtime;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public class ViewStartProvider : IViewStartProvider
|
||||||
|
{
|
||||||
|
private const string ViewStartFileName = "_ViewStart.cshtml";
|
||||||
|
private readonly string _appRoot;
|
||||||
|
private readonly IRazorPageFactory _pageFactory;
|
||||||
|
|
||||||
|
public ViewStartProvider(IApplicationEnvironment appEnv,
|
||||||
|
IRazorPageFactory pageFactory)
|
||||||
|
{
|
||||||
|
_appRoot = TrimTrailingSlash(appEnv.ApplicationBasePath);
|
||||||
|
_pageFactory = pageFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IEnumerable<IRazorPage> GetViewStartPages([NotNull] string path)
|
||||||
|
{
|
||||||
|
var viewStartLocations = GetViewStartLocations(path);
|
||||||
|
var viewStarts = viewStartLocations.Select(_pageFactory.CreateInstance)
|
||||||
|
.Where(p => p != null)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
// GetViewStartLocations return ViewStarts inside-out that is the _ViewStart closest to the page
|
||||||
|
// is the first: e.g. [ /Views/Home/_ViewStart, /Views/_ViewStart, /_ViewStart ]
|
||||||
|
// However they need to be executed outside in, so we'll reverse the sequence.
|
||||||
|
Array.Reverse(viewStarts);
|
||||||
|
|
||||||
|
return viewStarts;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal IEnumerable<string> GetViewStartLocations(string path)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(path))
|
||||||
|
{
|
||||||
|
return Enumerable.Empty<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var viewStartLocations = new List<string>();
|
||||||
|
var currentDir = GetViewDirectory(_appRoot, path);
|
||||||
|
while (IsSubDirectory(_appRoot, currentDir))
|
||||||
|
{
|
||||||
|
viewStartLocations.Add(Path.Combine(currentDir, ViewStartFileName));
|
||||||
|
currentDir = Path.GetDirectoryName(currentDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
return viewStartLocations;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsSubDirectory(string appRoot, string currentDir)
|
||||||
|
{
|
||||||
|
return currentDir.StartsWith(appRoot, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetViewDirectory(string appRoot, string viewPath)
|
||||||
|
{
|
||||||
|
if (viewPath.StartsWith("~/"))
|
||||||
|
{
|
||||||
|
viewPath = viewPath.Substring(2);
|
||||||
|
}
|
||||||
|
else if (viewPath[0] == Path.DirectorySeparatorChar ||
|
||||||
|
viewPath[0] == Path.AltDirectorySeparatorChar)
|
||||||
|
{
|
||||||
|
viewPath = viewPath.Substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var viewDir = Path.GetDirectoryName(viewPath);
|
||||||
|
return Path.GetFullPath(Path.Combine(appRoot, viewDir));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string TrimTrailingSlash(string path)
|
||||||
|
{
|
||||||
|
if (path.Length > 0 &&
|
||||||
|
path[path.Length - 1] == Path.DirectorySeparatorChar)
|
||||||
|
{
|
||||||
|
return path.Substring(0, path.Length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -30,14 +30,15 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IRazorPage CreateInstance([NotNull] string viewPath)
|
public IRazorPage CreateInstance([NotNull] string path)
|
||||||
{
|
{
|
||||||
var fileInfo = _fileInfoCache.GetFileInfo(viewPath);
|
var fileInfo = _fileInfoCache.GetFileInfo(path);
|
||||||
|
|
||||||
if (fileInfo != null)
|
if (fileInfo != null)
|
||||||
{
|
{
|
||||||
var result = _compilationService.Compile(fileInfo);
|
var result = _compilationService.Compile(fileInfo);
|
||||||
var page = (IRazorPage)_activator.CreateInstance(_serviceProvider, result.CompiledType);
|
var page = (IRazorPage)_activator.CreateInstance(_serviceProvider, result.CompiledType);
|
||||||
|
page.Path = path;
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ namespace Microsoft.AspNet.Mvc
|
||||||
yield return describe.Singleton<IViewEngineProvider, DefaultViewEngineProvider>();
|
yield return describe.Singleton<IViewEngineProvider, DefaultViewEngineProvider>();
|
||||||
yield return describe.Scoped<ICompositeViewEngine, CompositeViewEngine>();
|
yield return describe.Scoped<ICompositeViewEngine, CompositeViewEngine>();
|
||||||
yield return describe.Singleton<IRazorCompilationService, RazorCompilationService>();
|
yield return describe.Singleton<IRazorCompilationService, RazorCompilationService>();
|
||||||
|
yield return describe.Singleton<IViewStartProvider, ViewStartProvider>();
|
||||||
|
|
||||||
yield return describe.Singleton<IRazorPageActivator, RazorPageActivator>();
|
yield return describe.Singleton<IRazorPageActivator, RazorPageActivator>();
|
||||||
// Virtual path view factory needs to stay scoped so views can get get scoped services.
|
// Virtual path view factory needs to stay scoped so views can get get scoped services.
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
<Compile Include="RazorPageTest.cs" />
|
<Compile Include="RazorPageTest.cs" />
|
||||||
<Compile Include="RazorViewTest.cs" />
|
<Compile Include="RazorViewTest.cs" />
|
||||||
<Compile Include="SpanFactory.cs" />
|
<Compile Include="SpanFactory.cs" />
|
||||||
|
<Compile Include="ViewStartProviderTest.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
</Project>
|
</Project>
|
||||||
|
|
@ -160,15 +160,35 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
||||||
}, result.SearchedLocations);
|
}, result.SearchedLocations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FindView_ReturnsRazorView_IfLookupWasSuccessful()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var pageFactory = new Mock<IRazorPageFactory>();
|
||||||
|
pageFactory.Setup(p => p.CreateInstance(It.IsAny<string>()))
|
||||||
|
.Returns(Mock.Of<IRazorPage>());
|
||||||
|
var viewEngine = new RazorViewEngine(pageFactory.Object,
|
||||||
|
Mock.Of<IRazorPageActivator>(),
|
||||||
|
Mock.Of<IViewStartProvider>());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = viewEngine.FindView(_controllerTestContext, "test-view");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result.Success);
|
||||||
|
Assert.IsType<RazorView>(result.View);
|
||||||
|
Assert.Equal("/Views/bar/test-view.cshtml", result.ViewName);
|
||||||
|
}
|
||||||
|
|
||||||
private IViewEngine CreateSearchLocationViewEngineTester()
|
private IViewEngine CreateSearchLocationViewEngineTester()
|
||||||
{
|
{
|
||||||
var pageFactory = new Mock<IRazorPageFactory>();
|
var pageFactory = new Mock<IRazorPageFactory>();
|
||||||
pageFactory.Setup(vpf => vpf.CreateInstance(It.IsAny<string>()))
|
pageFactory.Setup(vpf => vpf.CreateInstance(It.IsAny<string>()))
|
||||||
.Returns<RazorPage>(null);
|
.Returns<RazorPage>(null);
|
||||||
|
|
||||||
var pageActivator = Mock.Of<IRazorPageActivator>();
|
var viewEngine = new RazorViewEngine(pageFactory.Object,
|
||||||
|
Mock.Of<IRazorPageActivator>(),
|
||||||
var viewEngine = new RazorViewEngine(pageFactory.Object, pageActivator);
|
Mock.Of<IViewStartProvider>());
|
||||||
|
|
||||||
return viewEngine;
|
return viewEngine;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http;
|
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
using Microsoft.AspNet.PipelineCore;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -27,6 +27,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
});
|
});
|
||||||
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
|
CreateViewStartProvider(),
|
||||||
page,
|
page,
|
||||||
executeViewHierarchy: false);
|
executeViewHierarchy: false);
|
||||||
var viewContext = CreateViewContext(view);
|
var viewContext = CreateViewContext(view);
|
||||||
|
|
@ -51,9 +52,10 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
Assert.Same(viewData, v.ViewContext.ViewData);
|
Assert.Same(viewData, v.ViewContext.ViewData);
|
||||||
});
|
});
|
||||||
var activator = new Mock<IRazorPageActivator>();
|
var activator = new Mock<IRazorPageActivator>();
|
||||||
|
|
||||||
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||||
activator.Object,
|
activator.Object,
|
||||||
|
CreateViewStartProvider(),
|
||||||
page,
|
page,
|
||||||
executeViewHierarchy: false);
|
executeViewHierarchy: false);
|
||||||
var viewContext = CreateViewContext(view);
|
var viewContext = CreateViewContext(view);
|
||||||
|
|
@ -86,6 +88,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||||
activator.Object,
|
activator.Object,
|
||||||
|
CreateViewStartProvider(),
|
||||||
page,
|
page,
|
||||||
executeViewHierarchy: false);
|
executeViewHierarchy: false);
|
||||||
var viewContext = CreateViewContext(view);
|
var viewContext = CreateViewContext(view);
|
||||||
|
|
@ -98,15 +101,17 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task RenderAsync_WithoutHierarchy_DoesNotExecuteLayoutPages()
|
public async Task RenderAsync_WithoutHierarchy_DoesNotExecuteLayoutOrViewStartPages()
|
||||||
{
|
{
|
||||||
var page = new TestableRazorPage(v =>
|
var page = new TestableRazorPage(v =>
|
||||||
{
|
{
|
||||||
v.Layout = LayoutPath;
|
v.Layout = LayoutPath;
|
||||||
});
|
});
|
||||||
var pageFactory = new Mock<IRazorPageFactory>();
|
var pageFactory = new Mock<IRazorPageFactory>();
|
||||||
|
var viewStartProvider = CreateViewStartProvider();
|
||||||
var view = new RazorView(pageFactory.Object,
|
var view = new RazorView(pageFactory.Object,
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
|
viewStartProvider,
|
||||||
page,
|
page,
|
||||||
executeViewHierarchy: false);
|
executeViewHierarchy: false);
|
||||||
var viewContext = CreateViewContext(view);
|
var viewContext = CreateViewContext(view);
|
||||||
|
|
@ -116,6 +121,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
pageFactory.Verify(v => v.CreateInstance(It.IsAny<string>()), Times.Never());
|
pageFactory.Verify(v => v.CreateInstance(It.IsAny<string>()), Times.Never());
|
||||||
|
Mock.Get(viewStartProvider).Verify(v => v.GetViewStartPages(It.IsAny<string>()), Times.Never());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -129,6 +135,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
});
|
});
|
||||||
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
|
CreateViewStartProvider(),
|
||||||
page,
|
page,
|
||||||
executeViewHierarchy: true);
|
executeViewHierarchy: true);
|
||||||
var viewContext = CreateViewContext(view);
|
var viewContext = CreateViewContext(view);
|
||||||
|
|
@ -152,6 +159,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
});
|
});
|
||||||
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
|
CreateViewStartProvider(),
|
||||||
page,
|
page,
|
||||||
executeViewHierarchy: true);
|
executeViewHierarchy: true);
|
||||||
var viewContext = CreateViewContext(view);
|
var viewContext = CreateViewContext(view);
|
||||||
|
|
@ -177,6 +185,49 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||||
activator.Object,
|
activator.Object,
|
||||||
|
CreateViewStartProvider(),
|
||||||
|
page,
|
||||||
|
executeViewHierarchy: true);
|
||||||
|
var viewContext = CreateViewContext(view);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await view.RenderAsync(viewContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
activator.Verify();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RenderAsync_WithHierarchy_ExecutesViewStart()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var actualLayoutPath = "";
|
||||||
|
var layoutPath = "/Views/_Shared/_Layout.cshtml";
|
||||||
|
var viewStart1 = new TestableRazorPage(v =>
|
||||||
|
{
|
||||||
|
v.Layout = "/fake-layout-path";
|
||||||
|
});
|
||||||
|
var viewStart2 = new TestableRazorPage(v =>
|
||||||
|
{
|
||||||
|
v.Layout = layoutPath;
|
||||||
|
});
|
||||||
|
var page = new TestableRazorPage(v =>
|
||||||
|
{
|
||||||
|
// This path must have been set as a consequence of running viewStart
|
||||||
|
actualLayoutPath = v.Layout;
|
||||||
|
// Clear out layout so we don't render it
|
||||||
|
v.Layout = null;
|
||||||
|
});
|
||||||
|
var activator = new Mock<IRazorPageActivator>();
|
||||||
|
activator.Setup(a => a.Activate(viewStart1, It.IsAny<ViewContext>()))
|
||||||
|
.Verifiable();
|
||||||
|
activator.Setup(a => a.Activate(viewStart2, It.IsAny<ViewContext>()))
|
||||||
|
.Verifiable();
|
||||||
|
activator.Setup(a => a.Activate(page, It.IsAny<ViewContext>()))
|
||||||
|
.Verifiable();
|
||||||
|
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||||
|
activator.Object,
|
||||||
|
CreateViewStartProvider(viewStart1, viewStart2),
|
||||||
page,
|
page,
|
||||||
executeViewHierarchy: true);
|
executeViewHierarchy: true);
|
||||||
var viewContext = CreateViewContext(view);
|
var viewContext = CreateViewContext(view);
|
||||||
|
|
@ -231,6 +282,7 @@ foot-content";
|
||||||
|
|
||||||
var view = new RazorView(pageFactory.Object,
|
var view = new RazorView(pageFactory.Object,
|
||||||
activator.Object,
|
activator.Object,
|
||||||
|
CreateViewStartProvider(),
|
||||||
page,
|
page,
|
||||||
executeViewHierarchy: true);
|
executeViewHierarchy: true);
|
||||||
var viewContext = CreateViewContext(view);
|
var viewContext = CreateViewContext(view);
|
||||||
|
|
@ -264,6 +316,7 @@ foot-content";
|
||||||
|
|
||||||
var view = new RazorView(pageFactory.Object,
|
var view = new RazorView(pageFactory.Object,
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
|
CreateViewStartProvider(),
|
||||||
page,
|
page,
|
||||||
executeViewHierarchy: true);
|
executeViewHierarchy: true);
|
||||||
var viewContext = CreateViewContext(view);
|
var viewContext = CreateViewContext(view);
|
||||||
|
|
@ -290,6 +343,7 @@ foot-content";
|
||||||
|
|
||||||
var view = new RazorView(pageFactory.Object,
|
var view = new RazorView(pageFactory.Object,
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
|
CreateViewStartProvider(),
|
||||||
page,
|
page,
|
||||||
executeViewHierarchy: true);
|
executeViewHierarchy: true);
|
||||||
var viewContext = CreateViewContext(view);
|
var viewContext = CreateViewContext(view);
|
||||||
|
|
@ -344,6 +398,7 @@ body-content";
|
||||||
|
|
||||||
var view = new RazorView(pageFactory.Object,
|
var view = new RazorView(pageFactory.Object,
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
|
CreateViewStartProvider(),
|
||||||
page,
|
page,
|
||||||
executeViewHierarchy: true);
|
executeViewHierarchy: true);
|
||||||
var viewContext = CreateViewContext(view);
|
var viewContext = CreateViewContext(view);
|
||||||
|
|
@ -357,8 +412,8 @@ body-content";
|
||||||
|
|
||||||
private static ViewContext CreateViewContext(RazorView view)
|
private static ViewContext CreateViewContext(RazorView view)
|
||||||
{
|
{
|
||||||
var httpContext = new Mock<HttpContext>();
|
var httpContext = new DefaultHttpContext();
|
||||||
var actionContext = new ActionContext(httpContext.Object, routeData: null, actionDescriptor: null);
|
var actionContext = new ActionContext(httpContext, routeData: null, actionDescriptor: null);
|
||||||
return new ViewContext(
|
return new ViewContext(
|
||||||
actionContext,
|
actionContext,
|
||||||
view,
|
view,
|
||||||
|
|
@ -366,6 +421,16 @@ body-content";
|
||||||
new StringWriter());
|
new StringWriter());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static IViewStartProvider CreateViewStartProvider(params IRazorPage[] viewStartPages)
|
||||||
|
{
|
||||||
|
viewStartPages = viewStartPages ?? new IRazorPage[0];
|
||||||
|
var viewStartProvider = new Mock<IViewStartProvider>();
|
||||||
|
viewStartProvider.Setup(v => v.GetViewStartPages(It.IsAny<string>()))
|
||||||
|
.Returns(viewStartPages);
|
||||||
|
|
||||||
|
return viewStartProvider.Object;
|
||||||
|
}
|
||||||
|
|
||||||
private class TestableRazorPage : RazorPage
|
private class TestableRazorPage : RazorPage
|
||||||
{
|
{
|
||||||
private readonly Action<TestableRazorPage> _executeAction;
|
private readonly Action<TestableRazorPage> _executeAction;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Microsoft.Framework.Runtime;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.Razor.Test
|
||||||
|
{
|
||||||
|
public class ViewStartProviderTest
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData(null)]
|
||||||
|
[InlineData("")]
|
||||||
|
public void GetViewStartLocations_ReturnsEmptySequenceIfViewPathIsEmpty(string viewPath)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var appPath = @"x:\test";
|
||||||
|
var provider = new ViewStartProvider(GetAppEnv(appPath), Mock.Of<IRazorPageFactory>());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = provider.GetViewStartLocations(viewPath);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Empty(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> GetViewStartLocations_ReturnsPotentialViewStartLocationsData
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
yield return new object[]
|
||||||
|
{
|
||||||
|
@"x:\test\myapp",
|
||||||
|
"/Views/Home/View.cshtml",
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
@"x:\test\myapp\Views\Home\_ViewStart.cshtml",
|
||||||
|
@"x:\test\myapp\Views\_ViewStart.cshtml",
|
||||||
|
@"x:\test\myapp\_ViewStart.cshtml",
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
yield return new object[]
|
||||||
|
{
|
||||||
|
@"x:\test\myapp",
|
||||||
|
"Views/Home/View.cshtml",
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
@"x:\test\myapp\Views\Home\_ViewStart.cshtml",
|
||||||
|
@"x:\test\myapp\Views\_ViewStart.cshtml",
|
||||||
|
@"x:\test\myapp\_ViewStart.cshtml",
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
yield return new object[]
|
||||||
|
{
|
||||||
|
@"x:\test\myapp\",
|
||||||
|
"Views/Home/View.cshtml",
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
@"x:\test\myapp\Views\Home\_ViewStart.cshtml",
|
||||||
|
@"x:\test\myapp\Views\_ViewStart.cshtml",
|
||||||
|
@"x:\test\myapp\_ViewStart.cshtml",
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData("GetViewStartLocations_ReturnsPotentialViewStartLocationsData")]
|
||||||
|
public void GetViewStartLocations_ReturnsPotentialViewStartLocations(string appPath,
|
||||||
|
string viewPath,
|
||||||
|
IEnumerable<string> expected)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var provider = new ViewStartProvider(GetAppEnv(appPath), Mock.Of<IRazorPageFactory>());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = provider.GetViewStartLocations(viewPath);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IApplicationEnvironment GetAppEnv(string appPath)
|
||||||
|
{
|
||||||
|
var appEnv = new Mock<IApplicationEnvironment>();
|
||||||
|
appEnv.Setup(p => p.ApplicationBasePath)
|
||||||
|
.Returns(appPath);
|
||||||
|
return appEnv.Object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue