Layout specification and discovery should follow the same behavior as
partials Fixes #1047
This commit is contained in:
parent
765f113515
commit
933f7eeb22
|
|
@ -0,0 +1,47 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>@ViewBag.Title - Ma Demande ASP.NET</title>
|
||||
<link rel="stylesheet" href="~/content/bootstrap.min.css" />
|
||||
<style>
|
||||
body {
|
||||
padding-top: 60px;
|
||||
}
|
||||
|
||||
@@media screen and (max-width: 768px) {
|
||||
body {
|
||||
padding-top: 0px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@await RenderSectionAsync("header", required: false)
|
||||
</head>
|
||||
<body>
|
||||
<div class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="/">Accueil</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container body-content">
|
||||
@RenderBody()
|
||||
<hr />
|
||||
<footer>
|
||||
<p>© @DateTime.Now.Year - Mon Application ASP.NET</p>
|
||||
</footer>
|
||||
</div>
|
||||
@await RenderSectionAsync("footer", required: false)
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
@{
|
||||
Layout = "/Views/Shared/_Layout.cshtml";
|
||||
Layout = "_Layout";
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// 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 Microsoft.AspNet.Mvc.Rendering;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
/// <summary>
|
||||
/// An <see cref="IViewEngine"/> used to render pages that use the Razor syntax.
|
||||
/// </summary>
|
||||
public interface IRazorViewEngine : IViewEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// Finds a <see cref="IRazorPage"/> using the same view discovery semantics used in
|
||||
/// <see cref="IViewEngine.FindPartialView(ActionContext, string)"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="ActionContext"/>.</param>
|
||||
/// <param name="viewName">The name or full path to the view.</param>
|
||||
/// <returns>A result representing the result of locating the <see cref="IRazorPage"/>.</returns>
|
||||
RazorPageResult FindPage(ActionContext context, string page);
|
||||
}
|
||||
}
|
||||
|
|
@ -13,9 +13,11 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
/// <summary>
|
||||
/// Creates a <see cref="RazorView"/> providing it with the <see cref="IRazorPage"/> to execute.
|
||||
/// </summary>
|
||||
/// <param name="viewEngine">The <see cref="IRazorViewEngine"/> that was used to locate Layout pages
|
||||
/// that will be part of <paramref name="page"/>'s execution.</param>
|
||||
/// <param name="page">The <see cref="IRazorPage"/> instance to execute.</param>
|
||||
/// <param name="isPartial">Determines if the view is to be executed as a partial.</param>
|
||||
/// <returns>A <see cref="IView"/> instance that renders the contents of the <paramref name="page"/></returns>
|
||||
IView GetView([NotNull] IRazorPage page, bool isPartial);
|
||||
IView GetView([NotNull] IRazorViewEngine viewEngine, [NotNull] IRazorPage page, bool isPartial);
|
||||
}
|
||||
}
|
||||
|
|
@ -75,7 +75,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// The layout view '{0}' could not be located.
|
||||
/// The layout view '{0}' could not be located. The following locations were searched:{1}
|
||||
/// </summary>
|
||||
internal static string LayoutCannotBeLocated
|
||||
{
|
||||
|
|
@ -83,11 +83,11 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// The layout view '{0}' could not be located.
|
||||
/// The layout view '{0}' could not be located. The following locations were searched:{1}
|
||||
/// </summary>
|
||||
internal static string FormatLayoutCannotBeLocated(object p0)
|
||||
internal static string FormatLayoutCannotBeLocated(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("LayoutCannotBeLocated"), p0);
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("LayoutCannotBeLocated"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
// 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>
|
||||
/// Represents the results of locating a <see cref="IRazorPage"/>.
|
||||
/// </summary>
|
||||
public class RazorPageResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="RazorPageResult"/> for a successful discovery.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the page that was located.</param>
|
||||
/// <param name="page">The located <see cref="IRazorPage"/>.</param>
|
||||
public RazorPageResult([NotNull] string name, [NotNull] IRazorPage page)
|
||||
{
|
||||
Name = name;
|
||||
Page = page;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="RazorPageResult"/> for an unsuccessful discovery.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the page that was located.</param>
|
||||
/// <param name="page">The locations that were searched.</param>
|
||||
public RazorPageResult([NotNull] string name, [NotNull] IEnumerable<string> searchedLocations)
|
||||
{
|
||||
Name = name;
|
||||
SearchedLocations = searchedLocations;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the page being located.
|
||||
/// </summary>
|
||||
/// <remarks>This property maps to the <c>name</c> parameter of
|
||||
/// <see cref="IRazorViewEngine.FindPage(ActionContext, string)"/>.</remarks>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IRazorPage"/> if found.
|
||||
/// </summary>
|
||||
/// <remarks>This property is <c>null</c> if the page was not found.</remarks>
|
||||
public IRazorPage Page { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the locations that were searched when <see cref="Page"/> could not be located.
|
||||
/// </summary>
|
||||
/// <remarks>This property is <c>null</c> if the page was found.</remarks>
|
||||
public IEnumerable<string> SearchedLocations { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -15,28 +15,28 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
/// </summary>
|
||||
public class RazorView : IView
|
||||
{
|
||||
private readonly IRazorPageFactory _pageFactory;
|
||||
private readonly IRazorViewEngine _viewEngine;
|
||||
private readonly IRazorPageActivator _pageActivator;
|
||||
private readonly IViewStartProvider _viewStartProvider;
|
||||
private IPageExecutionListenerFeature _pageExecutionFeature;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of RazorView
|
||||
/// Initializes a new instance of <see cref="RazorView"/>
|
||||
/// </summary>
|
||||
/// <param name="pageFactory">The page factory used to instantiate layout and _ViewStart pages.</param>
|
||||
/// <param name="viewEngine">The <see cref="IRazorViewEngine"/> used to locate Layout pages.</param>
|
||||
/// <param name="pageActivator">The <see cref="IRazorPageActivator"/> used to activate pages.</param>
|
||||
/// <param name="viewStartProvider">The <see cref="IViewStartProvider"/> used for discovery of _ViewStart
|
||||
/// <param name="razorPage">The <see cref="IRazorPage"/> instance to execute.</param>
|
||||
/// <param name="isPartial">Determines if the view is to be executed as a partial.</param>
|
||||
/// pages</param>
|
||||
public RazorView(IRazorPageFactory pageFactory,
|
||||
public RazorView(IRazorViewEngine viewEngine,
|
||||
IRazorPageActivator pageActivator,
|
||||
IViewStartProvider viewStartProvider,
|
||||
IRazorPage razorPage,
|
||||
bool isPartial
|
||||
)
|
||||
{
|
||||
_pageFactory = pageFactory;
|
||||
_viewEngine = viewEngine;
|
||||
_pageActivator = pageActivator;
|
||||
_viewStartProvider = viewStartProvider;
|
||||
RazorPage = razorPage;
|
||||
|
|
@ -186,12 +186,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
var layoutPage = _pageFactory.CreateInstance(previousPage.Layout);
|
||||
if (layoutPage == null)
|
||||
{
|
||||
var message = Resources.FormatLayoutCannotBeLocated(previousPage.Layout);
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
var layoutPage = GetLayoutPage(context, previousPage.Layout);
|
||||
|
||||
// Notify the previous page that any writes that are performed on it are part of sections being written
|
||||
// in the layout.
|
||||
|
|
@ -212,5 +207,19 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
await bodyWriter.CopyToAsync(context.Writer);
|
||||
}
|
||||
}
|
||||
|
||||
private IRazorPage GetLayoutPage(ViewContext context, string layoutPath)
|
||||
{
|
||||
var layoutPageResult = _viewEngine.FindPage(context, layoutPath);
|
||||
if (layoutPageResult.Page == null)
|
||||
{
|
||||
var locations = Environment.NewLine +
|
||||
string.Join(Environment.NewLine, layoutPageResult.SearchedLocations);
|
||||
throw new InvalidOperationException(Resources.FormatLayoutCannotBeLocated(layoutPath, locations));
|
||||
}
|
||||
|
||||
var layoutPage = layoutPageResult.Page;
|
||||
return layoutPage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ using Microsoft.AspNet.Mvc.Rendering;
|
|||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a view engine that is used to render a page that uses the Razor syntax.
|
||||
/// Default implementation of <see cref="IRazorViewEngine"/>.
|
||||
/// </summary>
|
||||
public class RazorViewEngine : IViewEngine
|
||||
public class RazorViewEngine : IRazorViewEngine
|
||||
{
|
||||
private const string ViewExtension = ".cshtml";
|
||||
internal const string ControllerKey = "controller";
|
||||
|
|
@ -78,7 +78,8 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(viewName));
|
||||
}
|
||||
|
||||
return CreateViewEngineResult(context, viewName, partial: false);
|
||||
var pageResult = GetRazorPageResult(context, viewName);
|
||||
return CreateViewEngineResult(pageResult, _viewFactory, isPartial: false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
@ -90,36 +91,49 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(partialViewName));
|
||||
}
|
||||
|
||||
return CreateViewEngineResult(context, partialViewName, partial: true);
|
||||
var pageResult = GetRazorPageResult(context, partialViewName);
|
||||
return CreateViewEngineResult(pageResult, _viewFactory, isPartial: true);
|
||||
}
|
||||
|
||||
private ViewEngineResult CreateViewEngineResult(ActionContext context,
|
||||
string viewName,
|
||||
bool partial)
|
||||
/// <inheritdoc />
|
||||
public RazorPageResult FindPage([NotNull] ActionContext context,
|
||||
string pageName)
|
||||
{
|
||||
var nameRepresentsPath = IsSpecificPath(viewName);
|
||||
|
||||
if (nameRepresentsPath)
|
||||
if (string.IsNullOrEmpty(pageName))
|
||||
{
|
||||
if (viewName.EndsWith(ViewExtension, StringComparison.OrdinalIgnoreCase))
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(pageName));
|
||||
}
|
||||
|
||||
return GetRazorPageResult(context, pageName);
|
||||
}
|
||||
|
||||
private RazorPageResult GetRazorPageResult(ActionContext context,
|
||||
string pageName)
|
||||
{
|
||||
if (IsApplicationRelativePath(pageName))
|
||||
{
|
||||
var applicationRelativePath = pageName;
|
||||
if (!pageName.EndsWith(ViewExtension, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var page = _pageFactory.CreateInstance(viewName);
|
||||
if (page != null)
|
||||
{
|
||||
return CreateFoundResult(context, page, viewName, partial);
|
||||
}
|
||||
applicationRelativePath += ViewExtension;
|
||||
}
|
||||
return ViewEngineResult.NotFound(viewName, new[] { viewName });
|
||||
|
||||
var page = _pageFactory.CreateInstance(applicationRelativePath);
|
||||
if (page != null)
|
||||
{
|
||||
return new RazorPageResult(pageName, page);
|
||||
}
|
||||
|
||||
return new RazorPageResult(pageName, new[] { pageName });
|
||||
}
|
||||
else
|
||||
{
|
||||
return LocateViewFromViewLocations(context, viewName, partial);
|
||||
return LocatePageFromViewLocations(context, pageName);
|
||||
}
|
||||
}
|
||||
|
||||
private ViewEngineResult LocateViewFromViewLocations(ActionContext context,
|
||||
string viewName,
|
||||
bool partial)
|
||||
private RazorPageResult LocatePageFromViewLocations(ActionContext context,
|
||||
string pageName)
|
||||
{
|
||||
// Initialize the dictionary for the typical case of having controller and action tokens.
|
||||
var routeValues = context.RouteData.Values;
|
||||
|
|
@ -129,7 +143,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
var viewLocations = !string.IsNullOrEmpty(areaName) ? AreaViewLocationFormats :
|
||||
ViewLocationFormats;
|
||||
|
||||
var expanderContext = new ViewLocationExpanderContext(context, viewName);
|
||||
var expanderContext = new ViewLocationExpanderContext(context, pageName);
|
||||
if (_viewLocationExpanders.Count > 0)
|
||||
{
|
||||
expanderContext.Values = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
|
|
@ -142,15 +156,15 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
}
|
||||
|
||||
// 2. With the values that we've accumumlated so far, check if we have a cached result.
|
||||
var viewLocation = _viewLocationCache.Get(expanderContext);
|
||||
if (!string.IsNullOrEmpty(viewLocation))
|
||||
var pageLocation = _viewLocationCache.Get(expanderContext);
|
||||
if (!string.IsNullOrEmpty(pageLocation))
|
||||
{
|
||||
var page = _pageFactory.CreateInstance(viewLocation);
|
||||
var page = _pageFactory.CreateInstance(pageLocation);
|
||||
|
||||
if (page != null)
|
||||
{
|
||||
// 2a. We found a IRazorPage at the cached location.
|
||||
return CreateFoundResult(context, page, viewName, partial);
|
||||
return new RazorPageResult(pageName, page);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -168,7 +182,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
{
|
||||
var transformedPath = string.Format(CultureInfo.InvariantCulture,
|
||||
path,
|
||||
viewName,
|
||||
pageName,
|
||||
controllerName,
|
||||
areaName);
|
||||
var page = _pageFactory.CreateInstance(transformedPath);
|
||||
|
|
@ -176,32 +190,30 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
{
|
||||
// 3a. We found a page. Cache the set of values that produced it and return a found result.
|
||||
_viewLocationCache.Set(expanderContext, transformedPath);
|
||||
return CreateFoundResult(context, page, transformedPath, partial);
|
||||
return new RazorPageResult(pageName, page);
|
||||
}
|
||||
|
||||
searchedLocations.Add(transformedPath);
|
||||
}
|
||||
|
||||
// 3b. We did not find a page for any of the paths.
|
||||
return ViewEngineResult.NotFound(viewName, searchedLocations);
|
||||
return new RazorPageResult(pageName, searchedLocations);
|
||||
}
|
||||
|
||||
private ViewEngineResult CreateFoundResult(ActionContext actionContext,
|
||||
IRazorPage page,
|
||||
string viewName,
|
||||
bool partial)
|
||||
private ViewEngineResult CreateViewEngineResult(RazorPageResult result,
|
||||
IRazorViewFactory razorViewFactory,
|
||||
bool isPartial)
|
||||
{
|
||||
// A single request could result in creating multiple IRazorView instances (for partials, view components)
|
||||
// and might store state. We'll use the service container to create new instances as we require.
|
||||
if (result.SearchedLocations != null)
|
||||
{
|
||||
return ViewEngineResult.NotFound(result.Name, result.SearchedLocations);
|
||||
}
|
||||
|
||||
var services = actionContext.HttpContext.RequestServices;
|
||||
|
||||
var view = _viewFactory.GetView(page, partial);
|
||||
|
||||
return ViewEngineResult.Found(viewName, view);
|
||||
var view = razorViewFactory.GetView(this, result.Page, isPartial);
|
||||
return ViewEngineResult.Found(result.Name, view);
|
||||
}
|
||||
|
||||
private static bool IsSpecificPath(string name)
|
||||
private static bool IsApplicationRelativePath(string name)
|
||||
{
|
||||
Debug.Assert(!string.IsNullOrEmpty(name));
|
||||
return name[0] == '~' || name[0] == '/';
|
||||
|
|
|
|||
|
|
@ -12,29 +12,27 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
public class RazorViewFactory : IRazorViewFactory
|
||||
{
|
||||
private readonly IRazorPageActivator _pageActivator;
|
||||
private readonly IRazorPageFactory _pageFactory;
|
||||
private readonly IViewStartProvider _viewStartProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of RazorViewFactory
|
||||
/// </summary>
|
||||
/// <param name="pageFactory">The page factory used to instantiate layout and _ViewStart pages.</param>
|
||||
/// <param name="pageActivator">The <see cref="IRazorPageActivator"/> used to activate pages.</param>
|
||||
/// <param name="viewStartProvider">The <see cref="IViewStartProvider"/> used for discovery of _ViewStart
|
||||
/// pages</param>
|
||||
public RazorViewFactory(IRazorPageFactory pageFactory,
|
||||
IRazorPageActivator pageActivator,
|
||||
public RazorViewFactory(IRazorPageActivator pageActivator,
|
||||
IViewStartProvider viewStartProvider)
|
||||
{
|
||||
_pageFactory = pageFactory;
|
||||
_pageActivator = pageActivator;
|
||||
_viewStartProvider = viewStartProvider;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IView GetView([NotNull] IRazorPage page, bool isPartial)
|
||||
public IView GetView([NotNull] IRazorViewEngine viewEngine,
|
||||
[NotNull] IRazorPage page,
|
||||
bool isPartial)
|
||||
{
|
||||
var razorView = new RazorView(_pageFactory, _pageActivator, _viewStartProvider, page, isPartial);
|
||||
var razorView = new RazorView(viewEngine, _pageActivator, _viewStartProvider, page, isPartial);
|
||||
return razorView;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@
|
|||
<value>The {0} returned by '{1}' must be an instance of '{2}'.</value>
|
||||
</data>
|
||||
<data name="LayoutCannotBeLocated" xml:space="preserve">
|
||||
<value>The layout view '{0}' could not be located.</value>
|
||||
<value>The layout view '{0}' could not be located. The following locations were searched:{1}</value>
|
||||
</data>
|
||||
<data name="LayoutCannotBeRendered" xml:space="preserve">
|
||||
<value>A layout page cannot be rendered after '{0}' has been invoked.</value>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.TestHost;
|
||||
using RazorWebSite;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||
{
|
||||
public class RazorViewLocationSpecificationTest
|
||||
{
|
||||
private const string BaseUrl = "http://localhost/ViewNameSpecification_Home/";
|
||||
private readonly IServiceProvider _provider = TestHelper.CreateServices("RazorWebSite");
|
||||
private readonly Action<IApplicationBuilder> _app = new Startup().Configure;
|
||||
|
||||
[Theory]
|
||||
[InlineData("LayoutSpecifiedWithPartialPathInViewStart")]
|
||||
[InlineData("LayoutSpecifiedWithPartialPathInViewStart_ForViewSpecifiedWithAppRelativePath")]
|
||||
[InlineData("LayoutSpecifiedWithPartialPathInViewStart_ForViewSpecifiedWithPartialName")]
|
||||
[InlineData("LayoutSpecifiedWithPartialPathInViewStart_ForViewSpecifiedWithAppRelativePathWithExtension")]
|
||||
public async Task PartialLayoutPaths_SpecifiedInViewStarts_GetResolvedByViewEngine(string action)
|
||||
{
|
||||
var expected =
|
||||
@"<layout>
|
||||
_ViewStart that specifies partial Layout
|
||||
</layout>";
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var body = await client.GetStringAsync(BaseUrl + action);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, body.Trim());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("LayoutSpecifiedWithPartialPathInPage")]
|
||||
[InlineData("LayoutSpecifiedWithPartialPathInPageWithPartialPath")]
|
||||
[InlineData("LayoutSpecifiedWithPartialPathInPageWithAppRelativePath")]
|
||||
[InlineData("LayoutSpecifiedWithPartialPathInPageWithAppRelativePathWithExtension")]
|
||||
public async Task PartialLayoutPaths_SpecifiedInPage_GetResolvedByViewEngine(string actionName)
|
||||
{
|
||||
var expected =
|
||||
@"<non-shared>
|
||||
Layout specified in page
|
||||
</non-shared>";
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var body = await client.GetStringAsync(BaseUrl + actionName);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, body.Trim());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("LayoutSpecifiedWithNonPartialPath")]
|
||||
[InlineData("LayoutSpecifiedWithNonPartialPathWithExtension")]
|
||||
public async Task NonPartialLayoutPaths_GetResolvedByViewEngine(string actionName)
|
||||
{
|
||||
var expected =
|
||||
@"<non-shared>
|
||||
Page With Non Partial Layout
|
||||
</non-shared>";
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var body = await client.GetStringAsync(BaseUrl + actionName);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, body.Trim());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("ViewWithPartial_SpecifiedWithPartialName")]
|
||||
[InlineData("ViewWithPartial_SpecifiedWithAbsoluteName")]
|
||||
[InlineData("ViewWithPartial_SpecifiedWithAbsoluteNameAndExtension")]
|
||||
public async Task PartialsCanBeSpecifiedWithPartialPath(string actionName)
|
||||
{
|
||||
var expected =
|
||||
@"<layout>
|
||||
|
||||
Non Shared Partial
|
||||
|
||||
</layout>";
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var body = await client.GetStringAsync(BaseUrl + actionName);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, body.Trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -216,6 +216,43 @@ component-content";
|
|||
Assert.Equal(expected, body.Trim());
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> RazorViewEngine_UsesExpandersForLayoutsData
|
||||
{
|
||||
get
|
||||
{
|
||||
var expected1 =
|
||||
@"<language-layout>
|
||||
View With Layout
|
||||
</language-layout>";
|
||||
|
||||
yield return new[] { "gb", expected1 };
|
||||
yield return new[] { "na", expected1 };
|
||||
|
||||
var expected2 =
|
||||
@"<fr-language-layout>
|
||||
View With Layout
|
||||
</fr-language-layout>";
|
||||
yield return new[] { "fr", expected2 };
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RazorViewEngine_UsesExpandersForLayoutsData))]
|
||||
public async Task RazorViewEngine_UsesExpandersForLayouts(string value, string expected)
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var body = await client.GetStringAsync("http://localhost/TemplateExpander/ViewWithLayout?language-expander-value=" +
|
||||
value);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, body.Trim());
|
||||
}
|
||||
|
||||
// Inheritance of chunks in _ViewStart is affected by paths being app-relative and not absolute.
|
||||
// This change ensures that _ViewStart files correctly inherits directives from parent _ViewStarts
|
||||
// which guarantees that paths flow correctly through MvcRazorHost.
|
||||
|
|
|
|||
|
|
@ -1,7 +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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Mvc.Razor.OptionDescriptors;
|
||||
|
|
@ -38,6 +37,33 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> ViewLocationExpanderTestData
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return new object[]
|
||||
{
|
||||
_controllerTestContext,
|
||||
new[]
|
||||
{
|
||||
"/Views/{1}/{0}.cshtml",
|
||||
"/Views/Shared/{0}.cshtml"
|
||||
}
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
_areaTestContext,
|
||||
new[]
|
||||
{
|
||||
"/Areas/{2}/Views/{1}/{0}.cshtml",
|
||||
"/Areas/{2}/Views/Shared/{0}.cshtml",
|
||||
"/Views/Shared/{0}.cshtml"
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
|
|
@ -90,11 +116,13 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
var viewFactory = new Mock<IRazorViewFactory>();
|
||||
var page = Mock.Of<IRazorPage>();
|
||||
var view = Mock.Of<IView>();
|
||||
|
||||
pageFactory.Setup(p => p.CreateInstance(It.IsAny<string>()))
|
||||
.Returns(Mock.Of<IRazorPage>());
|
||||
viewFactory.Setup(p => p.GetView(It.IsAny<IRazorPage>(), It.IsAny<bool>()))
|
||||
.Returns(Mock.Of<IView>()).Verifiable();
|
||||
viewFactory.Setup(p => p.GetView(It.IsAny<IRazorViewEngine>(), It.IsAny<IRazorPage>(), true))
|
||||
.Returns(view)
|
||||
.Verifiable();
|
||||
|
||||
var viewEngine = CreateViewEngine(pageFactory.Object, viewFactory.Object);
|
||||
var context = GetActionContext(_controllerTestContext);
|
||||
|
|
@ -104,8 +132,8 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
|
||||
// Assert
|
||||
Assert.True(result.Success);
|
||||
Assert.IsAssignableFrom<IView>(result.View);
|
||||
Assert.Equal("/Views/bar/test-view.cshtml", result.ViewName);
|
||||
Assert.Same(view, result.View);
|
||||
Assert.Equal("test-view", result.ViewName);
|
||||
viewFactory.Verify();
|
||||
}
|
||||
|
||||
|
|
@ -238,11 +266,13 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
var viewFactory = new Mock<IRazorViewFactory>();
|
||||
var page = Mock.Of<IRazorPage>();
|
||||
var view = Mock.Of<IView>();
|
||||
|
||||
pageFactory.Setup(p => p.CreateInstance(It.IsAny<string>()))
|
||||
.Returns(Mock.Of<IRazorPage>());
|
||||
viewFactory.Setup(p => p.GetView(It.IsAny<IRazorPage>(), It.IsAny<bool>()))
|
||||
.Returns(Mock.Of<IView>()).Verifiable();
|
||||
viewFactory.Setup(p => p.GetView(It.IsAny<IRazorViewEngine>(), It.IsAny<IRazorPage>(), false))
|
||||
.Returns(view)
|
||||
.Verifiable();
|
||||
|
||||
var viewEngine = CreateViewEngine(pageFactory.Object, viewFactory.Object);
|
||||
var context = GetActionContext(_controllerTestContext);
|
||||
|
|
@ -252,8 +282,8 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
|
||||
// Assert
|
||||
Assert.True(result.Success);
|
||||
Assert.IsAssignableFrom<IView>(result.View);
|
||||
Assert.Equal("/Views/bar/test-view.cshtml", result.ViewName);
|
||||
Assert.Same(view, result.View);
|
||||
Assert.Equal("test-view", result.ViewName);
|
||||
viewFactory.Verify();
|
||||
}
|
||||
|
||||
|
|
@ -303,35 +333,8 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
pageFactory.Verify();
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> FindView_UsesViewLocationExpandersToLocateViewsData
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return new object[]
|
||||
{
|
||||
_controllerTestContext,
|
||||
new[]
|
||||
{
|
||||
"/Views/{1}/{0}.cshtml",
|
||||
"/Views/Shared/{0}.cshtml"
|
||||
}
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
_areaTestContext,
|
||||
new[]
|
||||
{
|
||||
"/Areas/{2}/Views/{1}/{0}.cshtml",
|
||||
"/Areas/{2}/Views/Shared/{0}.cshtml",
|
||||
"/Views/Shared/{0}.cshtml"
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(FindView_UsesViewLocationExpandersToLocateViewsData))]
|
||||
[MemberData(nameof(ViewLocationExpanderTestData))]
|
||||
public void FindView_UsesViewLocationExpandersToLocateViews(IDictionary<string, object> routeValues,
|
||||
IEnumerable<string> expectedSeeds)
|
||||
{
|
||||
|
|
@ -342,7 +345,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
.Verifiable();
|
||||
|
||||
var viewFactory = new Mock<IRazorViewFactory>();
|
||||
viewFactory.Setup(p => p.GetView(It.IsAny<IRazorPage>(), It.IsAny<bool>()))
|
||||
viewFactory.Setup(p => p.GetView(It.IsAny<IRazorViewEngine>(), It.IsAny<IRazorPage>(), It.IsAny<bool>()))
|
||||
.Returns(Mock.Of<IView>());
|
||||
|
||||
var expander1Result = new[] { "some-seed" };
|
||||
|
|
@ -376,7 +379,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
|
||||
var viewEngine = CreateViewEngine(pageFactory.Object, viewFactory.Object,
|
||||
new[] { expander1.Object, expander2.Object });
|
||||
var context = GetActionContext(routeValues, viewFactory.Object);
|
||||
var context = GetActionContext(routeValues);
|
||||
|
||||
// Act
|
||||
var result = viewEngine.FindView(context, "test-view");
|
||||
|
|
@ -401,17 +404,17 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
.Verifiable();
|
||||
|
||||
var viewFactory = new Mock<IRazorViewFactory>();
|
||||
viewFactory.Setup(p => p.GetView(It.IsAny<IRazorPage>(), It.IsAny<bool>()))
|
||||
viewFactory.Setup(p => p.GetView(It.IsAny<IRazorViewEngine>(), It.IsAny<IRazorPage>(), false))
|
||||
.Returns(Mock.Of<IView>());
|
||||
|
||||
var cache = GetViewLocationCache();
|
||||
var cacheMock = Mock.Get<IViewLocationCache>(cache);
|
||||
var cacheMock = Mock.Get(cache);
|
||||
|
||||
cacheMock.Setup(c => c.Set(It.IsAny<ViewLocationExpanderContext>(), "/Views/Shared/baz.cshtml"))
|
||||
.Verifiable();
|
||||
|
||||
var viewEngine = CreateViewEngine(pageFactory.Object, viewFactory.Object, cache: cache);
|
||||
var context = GetActionContext(_controllerTestContext, viewFactory.Object);
|
||||
var context = GetActionContext(_controllerTestContext);
|
||||
|
||||
// Act
|
||||
var result = viewEngine.FindView(context, "baz");
|
||||
|
|
@ -432,7 +435,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
.Verifiable();
|
||||
|
||||
var viewFactory = new Mock<IRazorViewFactory>();
|
||||
viewFactory.Setup(p => p.GetView(It.IsAny<IRazorPage>(), It.IsAny<bool>()))
|
||||
viewFactory.Setup(p => p.GetView(It.IsAny<IRazorViewEngine>(), It.IsAny<IRazorPage>(), false))
|
||||
.Returns(Mock.Of<IView>());
|
||||
|
||||
var expander = new Mock<IViewLocationExpander>(MockBehavior.Strict);
|
||||
|
|
@ -472,7 +475,8 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
.Verifiable();
|
||||
|
||||
var viewFactory = new Mock<IRazorViewFactory>();
|
||||
viewFactory.Setup(p => p.GetView(It.IsAny<IRazorPage>(), It.IsAny<bool>())).Returns(Mock.Of<IView>());
|
||||
viewFactory.Setup(p => p.GetView(It.IsAny<IRazorViewEngine>(), It.IsAny<IRazorPage>(), false))
|
||||
.Returns(Mock.Of<IView>());
|
||||
|
||||
var cacheMock = new Mock<IViewLocationCache>();
|
||||
cacheMock.Setup(c => c.Get(It.IsAny<ViewLocationExpanderContext>()))
|
||||
|
|
@ -491,7 +495,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
viewFactory.Object,
|
||||
expanders: new[] { expander.Object },
|
||||
cache: cacheMock.Object);
|
||||
var context = GetActionContext(_controllerTestContext, viewFactory.Object);
|
||||
var context = GetActionContext(_controllerTestContext);
|
||||
|
||||
// Act
|
||||
var result = viewEngine.FindView(context, "baz");
|
||||
|
|
@ -503,10 +507,97 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
expander.Verify();
|
||||
}
|
||||
|
||||
private IViewEngine CreateViewEngine(IRazorPageFactory pageFactory = null,
|
||||
IRazorViewFactory viewFactory = null,
|
||||
IEnumerable<IViewLocationExpander> expanders = null,
|
||||
IViewLocationCache cache = null)
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
public void FindPage_ThrowsIfNameIsNullOrEmpty(string pageName)
|
||||
{
|
||||
// Arrange
|
||||
var viewEngine = CreateViewEngine();
|
||||
var context = GetActionContext(_controllerTestContext);
|
||||
|
||||
// Act & Assert
|
||||
ExceptionAssert.ThrowsArgumentNullOrEmpty(() => viewEngine.FindPage(context, pageName),
|
||||
"pageName");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ViewLocationExpanderTestData))]
|
||||
public void FindPage_UsesViewLocationExpander_ToExpandPaths(IDictionary<string, object> routeValues,
|
||||
IEnumerable<string> expectedSeeds)
|
||||
{
|
||||
// Arrange
|
||||
var page = Mock.Of<IRazorPage>();
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance("expanded-path/bar-layout"))
|
||||
.Returns(page)
|
||||
.Verifiable();
|
||||
|
||||
var viewFactory = new Mock<IRazorViewFactory>(MockBehavior.Strict);
|
||||
|
||||
var expander = new Mock<IViewLocationExpander>();
|
||||
expander.Setup(e => e.PopulateValues(It.IsAny<ViewLocationExpanderContext>()))
|
||||
.Callback((ViewLocationExpanderContext c) =>
|
||||
{
|
||||
Assert.NotNull(c.ActionContext);
|
||||
c.Values["expander-key"] = expander.ToString();
|
||||
})
|
||||
.Verifiable();
|
||||
expander.Setup(e => e.ExpandViewLocations(It.IsAny<ViewLocationExpanderContext>(),
|
||||
It.IsAny<IEnumerable<string>>()))
|
||||
.Returns((ViewLocationExpanderContext c, IEnumerable<string> seeds) =>
|
||||
{
|
||||
Assert.NotNull(c.ActionContext);
|
||||
Assert.Equal(expectedSeeds, seeds);
|
||||
|
||||
Assert.Equal(expander.ToString(), c.Values["expander-key"]);
|
||||
|
||||
return new[] { "expanded-path/bar-{0}" };
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
var viewEngine = CreateViewEngine(pageFactory.Object, viewFactory.Object,
|
||||
new[] { expander.Object });
|
||||
var context = GetActionContext(routeValues);
|
||||
|
||||
// Act
|
||||
var result = viewEngine.FindPage(context, "layout");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("layout", result.Name);
|
||||
Assert.Same(page, result.Page);
|
||||
Assert.Null(result.SearchedLocations);
|
||||
pageFactory.Verify();
|
||||
expander.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindPage_ReturnsSearchedLocationsIfPageCannotBeFound()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new[]
|
||||
{
|
||||
"/Views/bar/layout.cshtml",
|
||||
"/Views/Shared/layout.cshtml",
|
||||
};
|
||||
var page = Mock.Of<IRazorPage>();
|
||||
|
||||
var viewEngine = CreateViewEngine();
|
||||
var context = GetActionContext(_controllerTestContext);
|
||||
|
||||
// Act
|
||||
var result = viewEngine.FindPage(context, "layout");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("layout", result.Name);
|
||||
Assert.Null(result.Page);
|
||||
Assert.Equal(expected, result.SearchedLocations);
|
||||
}
|
||||
|
||||
private RazorViewEngine CreateViewEngine(IRazorPageFactory pageFactory = null,
|
||||
IRazorViewFactory viewFactory = null,
|
||||
IEnumerable<IViewLocationExpander> expanders = null,
|
||||
IViewLocationCache cache = null)
|
||||
{
|
||||
pageFactory = pageFactory ?? Mock.Of<IRazorPageFactory>();
|
||||
viewFactory = viewFactory ?? Mock.Of<IRazorViewFactory>();
|
||||
|
|
@ -541,14 +632,9 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
return cacheMock.Object;
|
||||
}
|
||||
|
||||
private static ActionContext GetActionContext(IDictionary<string, object> routeValues,
|
||||
IRazorViewFactory razorViewFactory = null)
|
||||
private static ActionContext GetActionContext(IDictionary<string, object> routeValues)
|
||||
{
|
||||
var httpContext = new DefaultHttpContext();
|
||||
var serviceProvider = new Mock<IServiceProvider>();
|
||||
|
||||
httpContext.RequestServices = serviceProvider.Object;
|
||||
|
||||
var routeData = new RouteData();
|
||||
foreach (var kvp in routeValues)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,11 +4,10 @@
|
|||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor.Test
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
public class RazorViewFactoryTest
|
||||
{
|
||||
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
|
|
@ -16,15 +15,17 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
{
|
||||
// Arrange
|
||||
var factory = new RazorViewFactory(
|
||||
Mock.Of<IRazorPageFactory>(),
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
Mock.Of<IViewStartProvider>());
|
||||
var page = Mock.Of<IRazorPage>();
|
||||
var viewEngine = Mock.Of<IRazorViewEngine>();
|
||||
|
||||
// Act
|
||||
var view = factory.GetView(Mock.Of<IRazorPage>(), isPartial);
|
||||
var view = factory.GetView(viewEngine, page, isPartial);
|
||||
|
||||
// Assert
|
||||
var razorView = Assert.IsType<RazorView>(view);
|
||||
Assert.Same(page, razorView.RazorPage);
|
||||
Assert.Equal(razorView.IsPartial, isPartial);
|
||||
}
|
||||
|
||||
|
|
@ -33,18 +34,19 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
{
|
||||
// Arrange
|
||||
var factory = new RazorViewFactory(
|
||||
Mock.Of<IRazorPageFactory>(),
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
Mock.Of<IViewStartProvider>());
|
||||
|
||||
var page = Mock.Of<IRazorPage>();
|
||||
var viewEngine = Mock.Of<IRazorViewEngine>();
|
||||
|
||||
// Act
|
||||
var view = factory.GetView(page, isPartial: false) as RazorView;
|
||||
var view = factory.GetView(viewEngine, page, isPartial: false);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(view);
|
||||
Assert.Same(view.RazorPage, page);
|
||||
var razorView = Assert.IsType<RazorView>(view);
|
||||
Assert.Same(razorView.RazorPage, page);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -28,7 +28,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
actual = v.Output;
|
||||
v.Write("Hello world");
|
||||
});
|
||||
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
page,
|
||||
|
|
@ -55,7 +55,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
Assert.Same(viewData, v.ViewContext.ViewData);
|
||||
});
|
||||
var activator = new Mock<IRazorPageActivator>();
|
||||
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
||||
activator.Object,
|
||||
CreateViewStartProvider(),
|
||||
page,
|
||||
|
|
@ -87,7 +87,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
var activator = new Mock<IRazorPageActivator>();
|
||||
activator.Setup(a => a.Activate(page, It.IsAny<ViewContext>()))
|
||||
.Verifiable();
|
||||
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
||||
activator.Object,
|
||||
CreateViewStartProvider(),
|
||||
page,
|
||||
|
|
@ -108,9 +108,9 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
{
|
||||
v.Layout = LayoutPath;
|
||||
});
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
var viewEngine = new Mock<IRazorViewEngine>();
|
||||
var viewStartProvider = CreateViewStartProvider();
|
||||
var view = new RazorView(pageFactory.Object,
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
viewStartProvider,
|
||||
page,
|
||||
|
|
@ -121,7 +121,8 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
await view.RenderAsync(viewContext);
|
||||
|
||||
// Assert
|
||||
pageFactory.Verify(v => v.CreateInstance(It.IsAny<string>()), Times.Never());
|
||||
viewEngine.Verify(v => v.FindPage(It.IsAny<ActionContext>(), It.IsAny<string>()),
|
||||
Times.Never());
|
||||
Mock.Get(viewStartProvider)
|
||||
.Verify(v => v.GetViewStartPages(It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
|
|
@ -135,7 +136,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
{
|
||||
actual = v.Output;
|
||||
});
|
||||
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
page,
|
||||
|
|
@ -159,7 +160,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
{
|
||||
v.WriteLiteral("Hello world");
|
||||
});
|
||||
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
page,
|
||||
|
|
@ -185,7 +186,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
var activator = new Mock<IRazorPageActivator>();
|
||||
activator.Setup(a => a.Activate(page, It.IsAny<ViewContext>()))
|
||||
.Verifiable();
|
||||
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
||||
activator.Object,
|
||||
CreateViewStartProvider(),
|
||||
page,
|
||||
|
|
@ -227,7 +228,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
.Verifiable();
|
||||
activator.Setup(a => a.Activate(page, It.IsAny<ViewContext>()))
|
||||
.Verifiable();
|
||||
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
||||
activator.Object,
|
||||
CreateViewStartProvider(viewStart1, viewStart2),
|
||||
page,
|
||||
|
|
@ -241,6 +242,42 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
activator.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RenderAsync_ThrowsIfLayoutPageCannotBeFound()
|
||||
{
|
||||
// Arrange
|
||||
var expected = string.Join(Environment.NewLine,
|
||||
"The layout view 'Does-Not-Exist-Layout' could not be located. " +
|
||||
"The following locations were searched:",
|
||||
"path1",
|
||||
"path2");
|
||||
|
||||
var layoutPath = "Does-Not-Exist-Layout";
|
||||
var page = new TestableRazorPage(v =>
|
||||
{
|
||||
v.Layout = layoutPath;
|
||||
});
|
||||
|
||||
var viewEngine = new Mock<IRazorViewEngine>();
|
||||
var activator = new Mock<IRazorPageActivator>();
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
Mock.Of<IViewStartProvider>(),
|
||||
page,
|
||||
isPartial: false);
|
||||
var viewContext = CreateViewContext(view);
|
||||
viewEngine.Setup(v => v.FindPage(viewContext, layoutPath))
|
||||
.Returns(new RazorPageResult(layoutPath, new[] { "path1", "path2" }))
|
||||
.Verifiable();
|
||||
|
||||
// Act
|
||||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => view.RenderAsync(viewContext));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, ex.Message);
|
||||
viewEngine.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RenderAsync_ExecutesLayoutPages()
|
||||
{
|
||||
|
|
@ -278,16 +315,17 @@ foot-content";
|
|||
.Verifiable();
|
||||
activator.Setup(a => a.Activate(layout, It.IsAny<ViewContext>()))
|
||||
.Verifiable();
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance(LayoutPath))
|
||||
.Returns(layout);
|
||||
var viewEngine = new Mock<IRazorViewEngine>();
|
||||
|
||||
var view = new RazorView(pageFactory.Object,
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
activator.Object,
|
||||
CreateViewStartProvider(),
|
||||
page,
|
||||
isPartial: false);
|
||||
var viewContext = CreateViewContext(view);
|
||||
viewEngine.Setup(p => p.FindPage(viewContext, LayoutPath))
|
||||
.Returns(new RazorPageResult(LayoutPath, layout))
|
||||
.Verifiable();
|
||||
|
||||
// Act
|
||||
await view.RenderAsync(viewContext);
|
||||
|
|
@ -296,6 +334,7 @@ foot-content";
|
|||
// Verify the activator was invoked for the primary page and layout page.
|
||||
activator.Verify();
|
||||
Assert.Equal(expected, viewContext.Writer.ToString());
|
||||
viewEngine.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -312,11 +351,11 @@ foot-content";
|
|||
{
|
||||
v.RenderBodyPublic();
|
||||
});
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance(LayoutPath))
|
||||
.Returns(layout);
|
||||
var viewEngine = new Mock<IRazorViewEngine>();
|
||||
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), LayoutPath))
|
||||
.Returns(new RazorPageResult(LayoutPath, layout));
|
||||
|
||||
var view = new RazorView(pageFactory.Object,
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
page,
|
||||
|
|
@ -339,11 +378,11 @@ foot-content";
|
|||
var layout = new TestableRazorPage(v =>
|
||||
{
|
||||
});
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance(LayoutPath))
|
||||
.Returns(layout);
|
||||
var viewEngine = new Mock<IRazorViewEngine>();
|
||||
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), LayoutPath))
|
||||
.Returns(new RazorPageResult(LayoutPath, layout));
|
||||
|
||||
var view = new RazorView(pageFactory.Object,
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
page,
|
||||
|
|
@ -389,13 +428,13 @@ body-content";
|
|||
v.Write(v.RenderSection("bar"));
|
||||
v.RenderBodyPublic();
|
||||
});
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance("~/Shared/Layout1.cshtml"))
|
||||
.Returns(layout1);
|
||||
pageFactory.Setup(p => p.CreateInstance("~/Shared/Layout2.cshtml"))
|
||||
.Returns(layout2);
|
||||
var viewEngine = new Mock<IRazorViewEngine>();
|
||||
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "~/Shared/Layout1.cshtml"))
|
||||
.Returns(new RazorPageResult("~/Shared/Layout1.cshtml", layout1));
|
||||
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "~/Shared/Layout2.cshtml"))
|
||||
.Returns(new RazorPageResult("~/Shared/Layout2.cshtml", layout2));
|
||||
|
||||
var view = new RazorView(pageFactory.Object,
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
page,
|
||||
|
|
@ -438,11 +477,11 @@ section-content-2";
|
|||
v.Write(v.RenderSection("foo"));
|
||||
});
|
||||
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance("layout-1"))
|
||||
.Returns(layout1);
|
||||
var viewEngine = new Mock<IRazorViewEngine>();
|
||||
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "layout-1"))
|
||||
.Returns(new RazorPageResult("layout-1", layout1));
|
||||
|
||||
var view = new RazorView(pageFactory.Object,
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
page,
|
||||
|
|
@ -483,11 +522,11 @@ section-content-2";
|
|||
v.Write(v.RenderSection("foo"));
|
||||
});
|
||||
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance("layout-1"))
|
||||
.Returns(layout1);
|
||||
var viewEngine = new Mock<IRazorViewEngine>();
|
||||
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "layout-1"))
|
||||
.Returns(new RazorPageResult("layout-1", layout1));
|
||||
|
||||
var view = new RazorView(pageFactory.Object,
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
page,
|
||||
|
|
@ -514,7 +553,7 @@ section-content-2";
|
|||
v.WriteLiteral("after-flush");
|
||||
});
|
||||
|
||||
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
page,
|
||||
|
|
@ -541,7 +580,7 @@ section-content-2";
|
|||
v.Layout = "~/Shared/Layout1.cshtml";
|
||||
v.WriteLiteral("body-content");
|
||||
});
|
||||
var layout1 = new TestableRazorPage(v =>
|
||||
var layoutPage = new TestableRazorPage(v =>
|
||||
{
|
||||
v.Write("layout-1" + Environment.NewLine);
|
||||
v.Write(v.RenderSection("foo"));
|
||||
|
|
@ -549,11 +588,12 @@ section-content-2";
|
|||
v.RenderBodyPublic();
|
||||
v.Layout = "~/Shared/Layout2.cshtml";
|
||||
});
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance("~/Shared/Layout1.cshtml"))
|
||||
.Returns(layout1);
|
||||
var viewEngine = new Mock<IRazorViewEngine>();
|
||||
var layoutPath = "~/Shared/Layout1.cshtml";
|
||||
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), layoutPath))
|
||||
.Returns(new RazorPageResult(layoutPath, layoutPage));
|
||||
|
||||
var view = new RazorView(pageFactory.Object,
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
page,
|
||||
|
|
@ -603,7 +643,7 @@ section-content-2";
|
|||
|
||||
var page = new TestableRazorPage(v =>
|
||||
{
|
||||
v.Layout = "/Layout.cshtml";
|
||||
v.Layout = "Layout";
|
||||
Assert.Same(pageWriter, v.Output);
|
||||
Assert.Same(pageContext, v.PageExecutionContext);
|
||||
});
|
||||
|
|
@ -619,14 +659,14 @@ section-content-2";
|
|||
});
|
||||
layout.Path = "/Layout.cshtml";
|
||||
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance("/Layout.cshtml"))
|
||||
.Returns(layout);
|
||||
var viewEngine = new Mock<IRazorViewEngine>();
|
||||
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "Layout"))
|
||||
.Returns(new RazorPageResult("Layout", layout));
|
||||
var viewStartProvider = new Mock<IViewStartProvider>();
|
||||
viewStartProvider.Setup(v => v.GetViewStartPages(It.IsAny<string>()))
|
||||
.Returns(Enumerable.Empty<IRazorPage>())
|
||||
.Verifiable();
|
||||
var view = new RazorView(pageFactory.Object,
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
viewStartProvider.Object,
|
||||
page,
|
||||
|
|
@ -670,7 +710,7 @@ section-content-2";
|
|||
});
|
||||
page.Path = "/MyPartialPage.cshtml";
|
||||
|
||||
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
Mock.Of<IViewStartProvider>(),
|
||||
page,
|
||||
|
|
@ -701,7 +741,7 @@ section-content-2";
|
|||
executed = true;
|
||||
});
|
||||
|
||||
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
Mock.Of<IViewStartProvider>(),
|
||||
page,
|
||||
|
|
@ -738,9 +778,9 @@ section-content-2";
|
|||
actualViewStart = v.Layout;
|
||||
v.Layout = expectedPage;
|
||||
});
|
||||
var pageFactory = Mock.Of<IRazorPageFactory>();
|
||||
var viewEngine = Mock.Of<IRazorViewEngine>();
|
||||
|
||||
var view = new RazorView(pageFactory,
|
||||
var view = new RazorView(viewEngine,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(viewStart1, viewStart2),
|
||||
page,
|
||||
|
|
@ -775,9 +815,9 @@ section-content-2";
|
|||
actual = v.Layout;
|
||||
v.Layout = null;
|
||||
});
|
||||
var pageFactory = Mock.Of<IRazorPageFactory>();
|
||||
var viewEngine = Mock.Of<IRazorViewEngine>();
|
||||
|
||||
var view = new RazorView(pageFactory,
|
||||
var view = new RazorView(viewEngine,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(viewStart1, viewStart2),
|
||||
page,
|
||||
|
|
@ -813,11 +853,11 @@ section-content-2";
|
|||
isPartialLayout = v.IsPartial;
|
||||
v.RenderBodyPublic();
|
||||
});
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance("/Layout.cshtml"))
|
||||
.Returns(layout);
|
||||
var viewEngine = new Mock<IRazorViewEngine>();
|
||||
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "/Layout.cshtml"))
|
||||
.Returns(new RazorPageResult("Layout", layout));
|
||||
|
||||
var view = new RazorView(pageFactory.Object,
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(viewStart),
|
||||
page,
|
||||
|
|
@ -842,7 +882,7 @@ section-content-2";
|
|||
{
|
||||
isPartialPage = v.IsPartial;
|
||||
});
|
||||
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
page,
|
||||
|
|
|
|||
|
|
@ -11,5 +11,10 @@ namespace RazorWebSite.Controllers
|
|||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
public ViewResult ViewWithLayout()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
// 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 Microsoft.AspNet.Mvc;
|
||||
|
||||
namespace RazorWebSite.Controllers
|
||||
{
|
||||
public class ViewNameSpecification_HomeController : Controller
|
||||
{
|
||||
public IActionResult LayoutSpecifiedWithPartialPathInViewStart()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
public IActionResult LayoutSpecifiedWithPartialPathInViewStart_ForViewSpecifiedWithPartialName()
|
||||
{
|
||||
return View("LayoutSpecifiedWithPartialPathInViewStart");
|
||||
}
|
||||
|
||||
public IActionResult LayoutSpecifiedWithPartialPathInViewStart_ForViewSpecifiedWithAppRelativePath()
|
||||
{
|
||||
return View("~/Views/ViewNameSpecification_Home/LayoutSpecifiedWithPartialPathInViewStart");
|
||||
}
|
||||
|
||||
public IActionResult LayoutSpecifiedWithPartialPathInViewStart_ForViewSpecifiedWithAppRelativePathWithExtension()
|
||||
{
|
||||
return View("~/Views/ViewNameSpecification_Home/LayoutSpecifiedWithPartialPathInViewStart.cshtml");
|
||||
}
|
||||
|
||||
public IActionResult LayoutSpecifiedWithPartialPathInPage()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
public IActionResult LayoutSpecifiedWithPartialPathInPageWithPartialPath()
|
||||
{
|
||||
return View("LayoutSpecifiedWithPartialPathInPage");
|
||||
}
|
||||
|
||||
public IActionResult LayoutSpecifiedWithPartialPathInPageWithAppRelativePath()
|
||||
{
|
||||
return View("~/Views/ViewNameSpecification_Home/LayoutSpecifiedWithPartialPathInPage");
|
||||
}
|
||||
|
||||
public IActionResult LayoutSpecifiedWithPartialPathInPageWithAppRelativePathWithExtension()
|
||||
{
|
||||
return View("~/Views/ViewNameSpecification_Home/LayoutSpecifiedWithPartialPathInPage.cshtml");
|
||||
}
|
||||
|
||||
public IActionResult LayoutSpecifiedWithNonPartialPath()
|
||||
{
|
||||
ViewData["Layout"] = "~/Views/ViewNameSpecification_Home/_NonSharedLayout";
|
||||
return View("PageWithNonPartialLayoutPath");
|
||||
}
|
||||
|
||||
public IActionResult LayoutSpecifiedWithNonPartialPathWithExtension()
|
||||
{
|
||||
ViewData["Layout"] = "~/Views/ViewNameSpecification_Home/_NonSharedLayout.cshtml";
|
||||
return View("PageWithNonPartialLayoutPath");
|
||||
}
|
||||
|
||||
public IActionResult ViewWithPartial_SpecifiedWithPartialName()
|
||||
{
|
||||
ViewBag.Partial = "NonSharedPartial";
|
||||
return View("ViewWithPartials");
|
||||
}
|
||||
|
||||
public IActionResult ViewWithPartial_SpecifiedWithAbsoluteName()
|
||||
{
|
||||
ViewBag.Partial = "~/Views/ViewNameSpecification_Home/NonSharedPartial";
|
||||
return View("ViewWithPartials");
|
||||
}
|
||||
|
||||
public IActionResult ViewWithPartial_SpecifiedWithAbsoluteNameAndExtension()
|
||||
{
|
||||
ViewBag.Partial = "~/Views/ViewNameSpecification_Home/NonSharedPartial.cshtml";
|
||||
return View("ViewWithPartials");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
@{
|
||||
Layout = "_LanguageLayout";
|
||||
}
|
||||
View With Layout
|
||||
|
|
@ -0,0 +1 @@
|
|||
<language-layout>@RenderBody()</language-layout>
|
||||
|
|
@ -0,0 +1 @@
|
|||
<fr-language-layout>@RenderBody()</fr-language-layout>
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
@{
|
||||
Layout = "_NonSharedLayout";
|
||||
}
|
||||
Layout specified in page
|
||||
|
|
@ -0,0 +1 @@
|
|||
_ViewStart that specifies partial Layout
|
||||
|
|
@ -0,0 +1 @@
|
|||
Non Shared Partial
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
@{
|
||||
Layout = (string)ViewData["Layout"];
|
||||
}
|
||||
Page With Non Partial Layout
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
@{
|
||||
var partial = (string)ViewData["Partial"];
|
||||
}
|
||||
@Html.Partial(partial)
|
||||
|
|
@ -0,0 +1 @@
|
|||
<non-shared>@RenderBody()</non-shared>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
@{
|
||||
Layout = "_Layout";
|
||||
}
|
||||
Loading…
Reference in New Issue