Layout specification and discovery should follow the same behavior as

partials

Fixes #1047
This commit is contained in:
Pranav K 2014-10-28 11:03:41 -07:00
parent 765f113515
commit 933f7eeb22
27 changed files with 706 additions and 186 deletions

View File

@ -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>&copy; @DateTime.Now.Year - Mon Application ASP.NET</p>
</footer>
</div>
@await RenderSectionAsync("footer", required: false)
</body>
</html>

View File

@ -1,3 +1,3 @@
@{
Layout = "/Views/Shared/_Layout.cshtml";
Layout = "_Layout";
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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>

View File

@ -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; }
}
}

View File

@ -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;
}
}
}

View File

@ -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] == '/';

View File

@ -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;
}
}

View File

@ -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>

View File

@ -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());
}
}
}

View File

@ -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.

View File

@ -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)
{

View File

@ -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);
}
}
}

View File

@ -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,

View File

@ -11,5 +11,10 @@ namespace RazorWebSite.Controllers
{
return View();
}
public ViewResult ViewWithLayout()
{
return View();
}
}
}

View File

@ -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");
}
}
}

View File

@ -0,0 +1,4 @@
@{
Layout = "_LanguageLayout";
}
View With Layout

View File

@ -0,0 +1 @@
<language-layout>@RenderBody()</language-layout>

View File

@ -0,0 +1 @@
<fr-language-layout>@RenderBody()</fr-language-layout>

View File

@ -0,0 +1,4 @@
@{
Layout = "_NonSharedLayout";
}
Layout specified in page

View File

@ -0,0 +1 @@
_ViewStart that specifies partial Layout

View File

@ -0,0 +1 @@
Non Shared Partial

View File

@ -0,0 +1,4 @@
@{
Layout = (string)ViewData["Layout"];
}
Page With Non Partial Layout

View File

@ -0,0 +1,4 @@
@{
var partial = (string)ViewData["Partial"];
}
@Html.Partial(partial)

View File

@ -0,0 +1 @@
<non-shared>@RenderBody()</non-shared>

View File

@ -0,0 +1,3 @@
@{
Layout = "_Layout";
}