diff --git a/src/Microsoft.AspNet.Mvc.Razor/IRazorViewEngine.cs b/src/Microsoft.AspNet.Mvc.Razor/IRazorViewEngine.cs
index 6a61b1844e..f2bcbf800d 100644
--- a/src/Microsoft.AspNet.Mvc.Razor/IRazorViewEngine.cs
+++ b/src/Microsoft.AspNet.Mvc.Razor/IRazorViewEngine.cs
@@ -11,12 +11,38 @@ namespace Microsoft.AspNet.Mvc.Razor
public interface IRazorViewEngine : IViewEngine
{
///
- /// Finds a using the same view discovery semantics used in
- /// .
+ /// Finds the page with the given using view locations and information from the
+ /// .
///
/// The .
- /// The name or full path to the view.
- /// A result representing the result of locating the .
- RazorPageResult FindPage(ActionContext context, string page);
+ /// The name of the page.
+ /// Determines if the page being found is a partial.
+ /// The of locating the page.
+ /// Page search semantics match .
+ RazorPageResult FindPage(ActionContext context, string pageName, bool isPartial);
+
+ ///
+ /// Gets the page with the given , relative to
+ /// unless is already absolute.
+ ///
+ /// The absolute path to the currently-executing page, if any.
+ /// The path to the page.
+ /// Determines if the page being found is a partial.
+ /// The of locating the page.
+ /// See also .
+ RazorPageResult GetPage(string executingFilePath, string pagePath, bool isPartial);
+
+ ///
+ /// Converts the given to be absolute, relative to
+ /// unless is already absolute.
+ ///
+ /// The absolute path to the currently-executing page, if any.
+ /// The path to the page.
+ ///
+ /// The combination of and if
+ /// is a relative path. The value (unchanged)
+ /// otherwise.
+ ///
+ string MakePathAbsolute(string executingFilePath, string pagePath);
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Razor/RazorPageResult.cs b/src/Microsoft.AspNet.Mvc.Razor/RazorPageResult.cs
index 89ee7a66e6..4d98254674 100644
--- a/src/Microsoft.AspNet.Mvc.Razor/RazorPageResult.cs
+++ b/src/Microsoft.AspNet.Mvc.Razor/RazorPageResult.cs
@@ -14,7 +14,7 @@ namespace Microsoft.AspNet.Mvc.Razor
///
/// Initializes a new instance of for a successful discovery.
///
- /// The name of the page that was located.
+ /// The name of the page that was found.
/// The located .
public RazorPageResult(string name, IRazorPage page)
{
@@ -36,7 +36,7 @@ namespace Microsoft.AspNet.Mvc.Razor
///
/// Initializes a new instance of for an unsuccessful discovery.
///
- /// The name of the page that was located.
+ /// The name of the page that was not found.
/// The locations that were searched.
public RazorPageResult(string name, IEnumerable searchedLocations)
{
@@ -56,10 +56,8 @@ namespace Microsoft.AspNet.Mvc.Razor
}
///
- /// Gets the name of the page being located.
+ /// Gets the name or the path of the page being located.
///
- /// This property maps to the name parameter of
- /// .
public string Name { get; }
///
@@ -69,7 +67,7 @@ namespace Microsoft.AspNet.Mvc.Razor
public IRazorPage Page { get; }
///
- /// Gets the locations that were searched when could not be located.
+ /// Gets the locations that were searched when could not be found.
///
/// This property is null if the page was found.
public IEnumerable SearchedLocations { get; }
diff --git a/src/Microsoft.AspNet.Mvc.Razor/RazorView.cs b/src/Microsoft.AspNet.Mvc.Razor/RazorView.cs
index 05b58c63ca..acffe82583 100644
--- a/src/Microsoft.AspNet.Mvc.Razor/RazorView.cs
+++ b/src/Microsoft.AspNet.Mvc.Razor/RazorView.cs
@@ -166,10 +166,15 @@ namespace Microsoft.AspNet.Mvc.Razor
{
var viewStart = ViewStartPages[i];
context.ExecutingFilePath = viewStart.Path;
+
// Copy the layout value from the previous view start (if any) to the current.
viewStart.Layout = layout;
+
await RenderPageCoreAsync(viewStart, context);
- layout = viewStart.Layout;
+
+ // Pass correct absolute path to next layout or the entry page if this view start set Layout to a
+ // relative path.
+ layout = _viewEngine.MakePathAbsolute(viewStart.Path, viewStart.Layout);
}
}
finally
@@ -177,13 +182,13 @@ namespace Microsoft.AspNet.Mvc.Razor
context.ExecutingFilePath = oldFilePath;
}
- // Copy over interesting properties from the ViewStart page to the entry page.
+ // Copy the layout value from the view start page(s) (if any) to the entry page.
RazorPage.Layout = layout;
}
private async Task RenderLayoutAsync(
ViewContext context,
- IBufferedTextWriter bodyWriter)
+ IBufferedTextWriter bodyWriter)
{
// A layout page can specify another layout page. We'll need to continue
// looking for layout pages until they're no longer specified.
@@ -202,7 +207,7 @@ namespace Microsoft.AspNet.Mvc.Razor
throw new InvalidOperationException(message);
}
- var layoutPage = GetLayoutPage(context, previousPage.Layout);
+ var layoutPage = GetLayoutPage(context, previousPage.Path, previousPage.Layout);
if (renderedLayouts.Count > 0 &&
renderedLayouts.Any(l => string.Equals(l.Path, layoutPage.Path, StringComparison.Ordinal)))
@@ -237,13 +242,18 @@ namespace Microsoft.AspNet.Mvc.Razor
}
}
- private IRazorPage GetLayoutPage(ViewContext context, string layoutPath)
+ private IRazorPage GetLayoutPage(ViewContext context, string executingFilePath, string layoutPath)
{
- var layoutPageResult = _viewEngine.FindPage(context, layoutPath);
+ var layoutPageResult = _viewEngine.GetPage(executingFilePath, layoutPath, isPartial: true);
if (layoutPageResult.Page == null)
{
- var locations = Environment.NewLine +
- string.Join(Environment.NewLine, layoutPageResult.SearchedLocations);
+ layoutPageResult = _viewEngine.FindPage(context, layoutPath, isPartial: true);
+ }
+
+ if (layoutPageResult.Page == null)
+ {
+ var locations =
+ Environment.NewLine + string.Join(Environment.NewLine, layoutPageResult.SearchedLocations);
throw new InvalidOperationException(Resources.FormatLayoutCannotBeLocated(layoutPath, locations));
}
diff --git a/src/Microsoft.AspNet.Mvc.Razor/RazorViewEngine.cs b/src/Microsoft.AspNet.Mvc.Razor/RazorViewEngine.cs
index e2cf7fc24d..11cea4997e 100644
--- a/src/Microsoft.AspNet.Mvc.Razor/RazorViewEngine.cs
+++ b/src/Microsoft.AspNet.Mvc.Razor/RazorViewEngine.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
+using System.Linq;
using System.Text.Encodings.Web;
using Microsoft.AspNet.Mvc.Routing;
using Microsoft.AspNet.Mvc.ViewEngines;
@@ -63,7 +64,7 @@ namespace Microsoft.AspNet.Mvc.Razor
/// which contains following indexes:
/// {0} - Action Name
/// {1} - Controller Name
- /// The values for these locations are case-sensitive on case-senstive file systems.
+ /// The values for these locations are case-sensitive on case-sensitive file systems.
/// For example, the view for the Test action of HomeController should be located at
/// /Views/Home/Test.cshtml. Locations such as /views/home/test.cshtml would not be discovered
///
@@ -84,7 +85,7 @@ namespace Microsoft.AspNet.Mvc.Razor
/// {0} - Action Name
/// {1} - Controller Name
/// {2} - Area name
- /// The values for these locations are case-sensitive on case-senstive file systems.
+ /// The values for these locations are case-sensitive on case-sensitive file systems.
/// For example, the view for the Test action of HomeController should be located at
/// /Views/Home/Test.cshtml. Locations such as /views/home/test.cshtml would not be discovered
///
@@ -100,65 +101,6 @@ namespace Microsoft.AspNet.Mvc.Razor
///
protected IMemoryCache ViewLookupCache { get; }
- ///
- public ViewEngineResult FindView(ActionContext context, string viewName)
- {
- if (context == null)
- {
- throw new ArgumentNullException(nameof(context));
- }
-
- if (string.IsNullOrEmpty(viewName))
- {
- throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(viewName));
- }
-
- var pageResult = GetViewLocationCacheResult(context, viewName, isPartial: false);
- return CreateViewEngineResult(pageResult, viewName, isPartial: false);
- }
-
- ///
- public ViewEngineResult FindPartialView(ActionContext context, string partialViewName)
- {
- if (context == null)
- {
- throw new ArgumentNullException(nameof(context));
- }
-
- if (string.IsNullOrEmpty(partialViewName))
- {
- throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(partialViewName));
- }
-
- var pageResult = GetViewLocationCacheResult(context, partialViewName, isPartial: true);
- return CreateViewEngineResult(pageResult, partialViewName, isPartial: true);
- }
-
- ///
- public RazorPageResult FindPage(ActionContext context, string pageName)
- {
- if (context == null)
- {
- throw new ArgumentNullException(nameof(context));
- }
-
- if (string.IsNullOrEmpty(pageName))
- {
- throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(pageName));
- }
-
- var cacheResult = GetViewLocationCacheResult(context, pageName, isPartial: true);
- if (cacheResult.Success)
- {
- var razorPage = cacheResult.ViewEntry.PageFactory();
- return new RazorPageResult(pageName, razorPage);
- }
- else
- {
- return new RazorPageResult(pageName, cacheResult.SearchedLocations);
- }
- }
-
///
/// Gets the case-normalized route value for the specified route .
///
@@ -232,29 +174,109 @@ namespace Microsoft.AspNet.Mvc.Razor
return stringRouteValue;
}
- private ViewLocationCacheResult GetViewLocationCacheResult(
- ActionContext context,
- string pageName,
- bool isPartial)
+ ///
+ public RazorPageResult FindPage(ActionContext context, string pageName, bool isPartial)
{
- if (IsApplicationRelativePath(pageName))
+ if (context == null)
{
- return LocatePageFromPath(pageName, isPartial);
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ if (string.IsNullOrEmpty(pageName))
+ {
+ throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(pageName));
+ }
+
+ if (IsApplicationRelativePath(pageName) || IsRelativePath(pageName))
+ {
+ // A path; not a name this method can handle.
+ return new RazorPageResult(pageName, Enumerable.Empty());
+ }
+
+ var cacheResult = LocatePageFromViewLocations(context, pageName, isPartial);
+ if (cacheResult.Success)
+ {
+ var razorPage = cacheResult.ViewEntry.PageFactory();
+ razorPage.IsPartial = isPartial;
+ return new RazorPageResult(pageName, razorPage);
}
else
{
- return LocatePageFromViewLocations(context, pageName, isPartial);
+ return new RazorPageResult(pageName, cacheResult.SearchedLocations);
}
}
- private ViewLocationCacheResult LocatePageFromPath(string pageName, bool isPartial)
+ ///
+ public RazorPageResult GetPage(string executingFilePath, string pagePath, bool isPartial)
{
- var applicationRelativePath = pageName;
- if (!pageName.EndsWith(ViewExtension, StringComparison.OrdinalIgnoreCase))
+ if (string.IsNullOrEmpty(pagePath))
{
- applicationRelativePath += ViewExtension;
+ throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(pagePath));
}
+ if (!(IsApplicationRelativePath(pagePath) || IsRelativePath(pagePath)))
+ {
+ // Not a path this method can handle.
+ return new RazorPageResult(pagePath, Enumerable.Empty());
+ }
+
+ var cacheResult = LocatePageFromPath(executingFilePath, pagePath, isPartial);
+ if (cacheResult.Success)
+ {
+ var razorPage = cacheResult.ViewEntry.PageFactory();
+ razorPage.IsPartial = isPartial;
+ return new RazorPageResult(pagePath, razorPage);
+ }
+ else
+ {
+ return new RazorPageResult(pagePath, cacheResult.SearchedLocations);
+ }
+ }
+
+ ///
+ public ViewEngineResult FindView(ActionContext context, string viewName, bool isPartial)
+ {
+ if (context == null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ if (string.IsNullOrEmpty(viewName))
+ {
+ throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(viewName));
+ }
+
+ if (IsApplicationRelativePath(viewName) || IsRelativePath(viewName))
+ {
+ // A path; not a name this method can handle.
+ return ViewEngineResult.NotFound(viewName, Enumerable.Empty());
+ }
+
+ var cacheResult = LocatePageFromViewLocations(context, viewName, isPartial);
+ return CreateViewEngineResult(cacheResult, viewName, isPartial);
+ }
+
+ ///
+ public ViewEngineResult GetView(string executingFilePath, string viewPath, bool isPartial)
+ {
+ if (string.IsNullOrEmpty(viewPath))
+ {
+ throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(viewPath));
+ }
+
+ if (!(IsApplicationRelativePath(viewPath) || IsRelativePath(viewPath)))
+ {
+ // Not a path this method can handle.
+ return ViewEngineResult.NotFound(viewPath, Enumerable.Empty());
+ }
+
+ var cacheResult = LocatePageFromPath(executingFilePath, viewPath, isPartial);
+ return CreateViewEngineResult(cacheResult, viewPath, isPartial);
+ }
+
+ private ViewLocationCacheResult LocatePageFromPath(string executingFilePath, string pagePath, bool isPartial)
+ {
+ var applicationRelativePath = MakePathAbsolute(executingFilePath, pagePath);
var cacheKey = new ViewLocationCacheKey(applicationRelativePath, isPartial);
ViewLocationCacheResult cacheResult;
if (!ViewLookupCache.TryGetValue(cacheKey, out cacheResult))
@@ -272,7 +294,7 @@ namespace Microsoft.AspNet.Mvc.Razor
// No views were found at the specified location. Create a not found result.
if (cacheResult == null)
{
- cacheResult = new ViewLocationCacheResult(new[] { pageName });
+ cacheResult = new ViewLocationCacheResult(new[] { applicationRelativePath });
}
cacheResult = ViewLookupCache.Set(
@@ -327,6 +349,42 @@ namespace Microsoft.AspNet.Mvc.Razor
return cacheResult;
}
+ ///
+ public string MakePathAbsolute(string executingFilePath, string pagePath)
+ {
+ if (string.IsNullOrEmpty(pagePath))
+ {
+ // Path is not valid; no change required.
+ return pagePath;
+ }
+
+ if (IsApplicationRelativePath(pagePath))
+ {
+ // An absolute path already; no change required.
+ return pagePath;
+ }
+
+ if (!IsRelativePath(pagePath))
+ {
+ // A page name; no change required.
+ return pagePath;
+ }
+
+ // Given a relative path i.e. not yet application-relative (starting with "~/" or "/"), interpret
+ // path relative to currently-executing view, if any.
+ if (string.IsNullOrEmpty(executingFilePath))
+ {
+ // Not yet executing a view. Start in app root.
+ return "/" + pagePath;
+ }
+
+ // Get directory name (including final slash) but do not use Path.GetDirectoryName() to preserve path
+ // normalization.
+ var index = executingFilePath.LastIndexOf('/');
+ Debug.Assert(index >= 0);
+ return executingFilePath.Substring(0, index + 1) + pagePath;
+ }
+
private ViewLocationCacheResult OnCacheMiss(
ViewLocationExpanderContext expanderContext,
ViewLocationCacheKey cacheKey)
@@ -451,7 +509,7 @@ namespace Microsoft.AspNet.Mvc.Razor
for (var i = 0; i < viewStarts.Length; i++)
{
var viewStartItem = result.ViewStartEntries[i];
- viewStarts[i] = result.ViewStartEntries[i].PageFactory();
+ viewStarts[i] = viewStartItem.PageFactory();
}
var view = new RazorView(
@@ -469,5 +527,13 @@ namespace Microsoft.AspNet.Mvc.Razor
Debug.Assert(!string.IsNullOrEmpty(name));
return name[0] == '~' || name[0] == '/';
}
+
+ private static bool IsRelativePath(string name)
+ {
+ Debug.Assert(!string.IsNullOrEmpty(name));
+
+ // Though ./ViewName looks like a relative path, framework searches for that view using view locations.
+ return name.EndsWith(ViewExtension, StringComparison.OrdinalIgnoreCase);
+ }
}
}
diff --git a/src/Microsoft.AspNet.Mvc.ViewFeatures/Rendering/ViewContext.cs b/src/Microsoft.AspNet.Mvc.ViewFeatures/Rendering/ViewContext.cs
index c342295ceb..ec3569eed4 100644
--- a/src/Microsoft.AspNet.Mvc.ViewFeatures/Rendering/ViewContext.cs
+++ b/src/Microsoft.AspNet.Mvc.ViewFeatures/Rendering/ViewContext.cs
@@ -130,6 +130,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
ValidationSummaryMessageElement = viewContext.ValidationSummaryMessageElement;
ValidationMessageElement = viewContext.ValidationMessageElement;
+ ExecutingFilePath = viewContext.ExecutingFilePath;
View = view;
ViewData = viewData;
TempData = viewContext.TempData;
diff --git a/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewComponents/ViewViewComponentResult.cs b/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewComponents/ViewViewComponentResult.cs
index 728d7fecc4..3239e763c9 100644
--- a/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewComponents/ViewViewComponentResult.cs
+++ b/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewComponents/ViewViewComponentResult.cs
@@ -76,18 +76,19 @@ namespace Microsoft.AspNet.Mvc.ViewComponents
throw new ArgumentNullException(nameof(context));
}
- var viewEngine = ViewEngine ?? ResolveViewEngine(context);
+ var viewContext = context.ViewContext;
var viewData = ViewData ?? context.ViewData;
+ var viewEngine = ViewEngine ?? ResolveViewEngine(context);
var isNullOrEmptyViewName = string.IsNullOrEmpty(ViewName);
- string qualifiedViewName;
- if (!isNullOrEmptyViewName &&
- (ViewName[0] == '~' || ViewName[0] == '/'))
+ ViewEngineResult result = null;
+ if (!isNullOrEmptyViewName)
{
- // View name that was passed in is already a rooted path, the view engine will handle this.
- qualifiedViewName = ViewName;
+ // If view name was passed in is already a path, the view engine will handle this.
+ result = viewEngine.GetView(viewContext.ExecutingFilePath, ViewName, isPartial: true);
}
- else
+
+ if (result == null || !result.Success)
{
// This will produce a string like:
//
@@ -101,42 +102,32 @@ namespace Microsoft.AspNet.Mvc.ViewComponents
//
// This supports a controller or area providing an override for component views.
var viewName = isNullOrEmptyViewName ? DefaultViewName : ViewName;
-
- qualifiedViewName = string.Format(
+ var qualifiedViewName = string.Format(
CultureInfo.InvariantCulture,
ViewPathFormat,
context.ViewComponentDescriptor.ShortName,
viewName);
+
+ result = viewEngine.FindView(viewContext, qualifiedViewName, isPartial: true);
}
- var view = FindView(context.ViewContext, viewEngine, qualifiedViewName);
-
- var childViewContext = new ViewContext(
- context.ViewContext,
- view,
- ViewData ?? context.ViewData,
- context.Writer);
-
+ var view = result.EnsureSuccessful().View;
using (view as IDisposable)
{
if (_diagnosticSource == null)
{
- _diagnosticSource = context.ViewContext.HttpContext.RequestServices.GetRequiredService();
+ _diagnosticSource = viewContext.HttpContext.RequestServices.GetRequiredService();
}
_diagnosticSource.ViewComponentBeforeViewExecute(context, view);
+ var childViewContext = new ViewContext(viewContext, view, ViewData ?? context.ViewData, context.Writer);
await view.RenderAsync(childViewContext);
_diagnosticSource.ViewComponentAfterViewExecute(context, view);
}
}
- private static IView FindView(ActionContext context, IViewEngine viewEngine, string viewName)
- {
- return viewEngine.FindPartialView(context, viewName).EnsureSuccessful().View;
- }
-
private static IViewEngine ResolveViewEngine(ViewComponentContext context)
{
return context.ViewContext.HttpContext.RequestServices.GetRequiredService();
diff --git a/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewEngines/CompositeViewEngine.cs b/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewEngines/CompositeViewEngine.cs
index d8f9502a8b..28de971413 100644
--- a/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewEngines/CompositeViewEngine.cs
+++ b/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewEngines/CompositeViewEngine.cs
@@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation. 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.Extensions.OptionsModel;
@@ -11,6 +10,8 @@ namespace Microsoft.AspNet.Mvc.ViewEngines
///
public class CompositeViewEngine : ICompositeViewEngine
{
+ private const string ViewExtension = ".cshtml";
+
///
/// Initializes a new instance of .
///
@@ -24,61 +25,53 @@ namespace Microsoft.AspNet.Mvc.ViewEngines
public IReadOnlyList ViewEngines { get; }
///
- public ViewEngineResult FindPartialView(
- ActionContext context,
- string partialViewName)
+ public ViewEngineResult FindView(ActionContext context, string viewName, bool isPartial)
{
- if (context == null)
- {
- throw new ArgumentNullException(nameof(context));
- }
-
- if (partialViewName == null)
- {
- throw new ArgumentNullException(nameof(partialViewName));
- }
-
- return FindView(context, partialViewName, partial: true);
- }
-
- ///
- public ViewEngineResult FindView(
- ActionContext context,
- string viewName)
- {
- if (context == null)
- {
- throw new ArgumentNullException(nameof(context));
- }
-
- if (viewName == null)
- {
- throw new ArgumentNullException(nameof(viewName));
- }
-
- return FindView(context, viewName, partial: false);
- }
-
- private ViewEngineResult FindView(
- ActionContext context,
- string viewName,
- bool partial)
- {
- var searchedLocations = Enumerable.Empty();
+ List searchedLocations = null;
foreach (var engine in ViewEngines)
{
- var result = partial ? engine.FindPartialView(context, viewName) :
- engine.FindView(context, viewName);
-
+ var result = engine.FindView(context, viewName, isPartial);
if (result.Success)
{
return result;
}
- searchedLocations = searchedLocations.Concat(result.SearchedLocations);
+ if (searchedLocations == null)
+ {
+ searchedLocations = new List(result.SearchedLocations);
+ }
+ else
+ {
+ searchedLocations.AddRange(result.SearchedLocations);
+ }
}
- return ViewEngineResult.NotFound(viewName, searchedLocations);
+ return ViewEngineResult.NotFound(viewName, searchedLocations ?? Enumerable.Empty());
+ }
+
+ ///
+ public ViewEngineResult GetView(string executingFilePath, string viewPath, bool isPartial)
+ {
+ List searchedLocations = null;
+ foreach (var engine in ViewEngines)
+ {
+ var result = engine.GetView(executingFilePath, viewPath, isPartial);
+ if (result.Success)
+ {
+ return result;
+ }
+
+ if (searchedLocations == null)
+ {
+ searchedLocations = new List(result.SearchedLocations);
+ }
+ else
+ {
+ searchedLocations.AddRange(result.SearchedLocations);
+ }
+ }
+
+ return ViewEngineResult.NotFound(viewPath, searchedLocations ?? Enumerable.Empty());
}
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewEngines/IViewEngine.cs b/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewEngines/IViewEngine.cs
index a12719e993..72811b6312 100644
--- a/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewEngines/IViewEngine.cs
+++ b/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewEngines/IViewEngine.cs
@@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
namespace Microsoft.AspNet.Mvc.ViewEngines
{
///
@@ -10,19 +9,23 @@ namespace Microsoft.AspNet.Mvc.ViewEngines
public interface IViewEngine
{
///
- /// Finds the specified view by using the specified action context.
+ /// Finds the view with the given using view locations and information from the
+ /// .
///
- /// The action context.
- /// The name or full path to the view.
- /// A result representing the result of locating the view.
- ViewEngineResult FindView(ActionContext context, string viewName);
+ /// The .
+ /// The name of the view.
+ /// Determines if the view being found is a partial.
+ /// The of locating the view.
+ ViewEngineResult FindView(ActionContext context, string viewName, bool isPartial);
///
- /// Finds the specified partial view by using the specified action context.
+ /// Gets the view with the given , relative to
+ /// unless is already absolute.
///
- /// The action context.
- /// The name or full path to the view.
- /// A result representing the result of locating the view.
- ViewEngineResult FindPartialView(ActionContext context, string partialViewName);
+ /// The absolute path to the currently-executing view, if any.
+ /// The path to the view.
+ /// Determines if the view being found is a partial.
+ /// The of locating the view.
+ ViewEngineResult GetView(string executingFilePath, string viewPath, bool isPartial);
}
}
diff --git a/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewFeatures/HtmlHelper.cs b/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewFeatures/HtmlHelper.cs
index a0b0d78d4e..30f682a39a 100644
--- a/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewFeatures/HtmlHelper.cs
+++ b/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewFeatures/HtmlHelper.cs
@@ -510,39 +510,45 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
return RenderPartialCoreAsync(partialViewName, model, viewData, ViewContext.Writer);
}
- protected virtual IHtmlContent GenerateDisplay(ModelExplorer modelExplorer,
- string htmlFieldName,
- string templateName,
- object additionalViewData)
+ protected virtual IHtmlContent GenerateDisplay(
+ ModelExplorer modelExplorer,
+ string htmlFieldName,
+ string templateName,
+ object additionalViewData)
{
- var templateBuilder = new TemplateBuilder(_viewEngine,
- ViewContext,
- ViewData,
- modelExplorer,
- htmlFieldName,
- templateName,
- readOnly: true,
- additionalViewData: additionalViewData);
+ var templateBuilder = new TemplateBuilder(
+ _viewEngine,
+ ViewContext,
+ ViewData,
+ modelExplorer,
+ htmlFieldName,
+ templateName,
+ readOnly: true,
+ additionalViewData: additionalViewData);
return templateBuilder.Build();
}
- protected virtual async Task RenderPartialCoreAsync(string partialViewName,
- object model,
- ViewDataDictionary viewData,
- TextWriter writer)
+ protected virtual async Task RenderPartialCoreAsync(
+ string partialViewName,
+ object model,
+ ViewDataDictionary viewData,
+ TextWriter writer)
{
if (partialViewName == null)
{
throw new ArgumentNullException(nameof(partialViewName));
}
- // Determine which ViewData we should use to construct a new ViewData
- var baseViewData = viewData ?? ViewData;
+ var viewEngineResult = _viewEngine.GetView(
+ ViewContext.ExecutingFilePath,
+ partialViewName,
+ isPartial: true);
+ if (!viewEngineResult.Success)
+ {
+ viewEngineResult = _viewEngine.FindView(ViewContext, partialViewName, isPartial: true);
+ }
- var newViewData = new ViewDataDictionary(baseViewData, model);
-
- var viewEngineResult = _viewEngine.FindPartialView(ViewContext, partialViewName);
if (!viewEngineResult.Success)
{
var locations = string.Empty;
@@ -559,7 +565,12 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
var view = viewEngineResult.View;
using (view as IDisposable)
{
+ // Determine which ViewData we should use to construct a new ViewData
+ var baseViewData = viewData ?? ViewData;
+
+ var newViewData = new ViewDataDictionary(baseViewData, model);
var viewContext = new ViewContext(ViewContext, view, newViewData, writer);
+
await viewEngineResult.View.RenderAsync(viewContext);
}
}
diff --git a/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewFeatures/PartialViewResultExecutor.cs b/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewFeatures/PartialViewResultExecutor.cs
index 99e23f11cb..5a90cf8eec 100644
--- a/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewFeatures/PartialViewResultExecutor.cs
+++ b/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewFeatures/PartialViewResultExecutor.cs
@@ -69,7 +69,12 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
var viewEngine = viewResult.ViewEngine ?? ViewEngine;
var viewName = viewResult.ViewName ?? actionContext.ActionDescriptor.Name;
- var result = viewEngine.FindPartialView(actionContext, viewName);
+ var result = viewEngine.GetView(executingFilePath: null, viewPath: viewName, isPartial: true);
+ if (!result.Success)
+ {
+ result = viewEngine.FindView(actionContext, viewName, isPartial: true);
+ }
+
if (result.Success)
{
DiagnosticSource.ViewFound(actionContext, true, viewResult, viewName, result.View);
diff --git a/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewFeatures/TemplateRenderer.cs b/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewFeatures/TemplateRenderer.cs
index 3d6c26c1b3..8846fa28c5 100644
--- a/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewFeatures/TemplateRenderer.cs
+++ b/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewFeatures/TemplateRenderer.cs
@@ -109,9 +109,14 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures.Internal
foreach (string viewName in GetViewNames())
{
- var fullViewName = modeViewPath + "/" + viewName;
+ var viewEngineResult = _viewEngine.GetView(_viewContext.ExecutingFilePath, viewName, isPartial: true);
+ if (!viewEngineResult.Success)
+ {
+ // Success here is more common than with GetView() but GetView() is less expensive.
+ var fullViewName = modeViewPath + "/" + viewName;
+ viewEngineResult = _viewEngine.FindView(_viewContext, fullViewName, isPartial: true);
+ }
- var viewEngineResult = _viewEngine.FindPartialView(_viewContext, fullViewName);
if (viewEngineResult.Success)
{
using (var writer = new StringCollectionTextWriter(_viewContext.Writer.Encoding))
diff --git a/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewFeatures/ViewResultExecutor.cs b/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewFeatures/ViewResultExecutor.cs
index ced91f96b9..2dd06ae420 100644
--- a/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewFeatures/ViewResultExecutor.cs
+++ b/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewFeatures/ViewResultExecutor.cs
@@ -68,7 +68,12 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
var viewEngine = viewResult.ViewEngine ?? ViewEngine;
var viewName = viewResult.ViewName ?? actionContext.ActionDescriptor.Name;
- var result = viewEngine.FindView(actionContext, viewName);
+ var result = viewEngine.GetView(executingFilePath: null, viewPath: viewName, isPartial: false);
+ if (!result.Success)
+ {
+ result = viewEngine.FindView(actionContext, viewName, isPartial: false);
+ }
+
if (result.Success)
{
if (DiagnosticSource.IsEnabled("Microsoft.AspNet.Mvc.ViewFound"))
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/RazorViewLocationSpecificationTest.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/RazorViewLocationSpecificationTest.cs
index 6ff2c9cd13..a656033917 100644
--- a/test/Microsoft.AspNet.Mvc.FunctionalTests/RazorViewLocationSpecificationTest.cs
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/RazorViewLocationSpecificationTest.cs
@@ -20,9 +20,9 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
[Theory]
[InlineData("LayoutSpecifiedWithPartialPathInViewStart")]
- [InlineData("LayoutSpecifiedWithPartialPathInViewStart_ForViewSpecifiedWithAppRelativePath")]
+ [InlineData("LayoutSpecifiedWithPartialPathInViewStart_ForViewSpecifiedWithRelativePath")]
[InlineData("LayoutSpecifiedWithPartialPathInViewStart_ForViewSpecifiedWithPartialName")]
- [InlineData("LayoutSpecifiedWithPartialPathInViewStart_ForViewSpecifiedWithAppRelativePathWithExtension")]
+ [InlineData("LayoutSpecifiedWithPartialPathInViewStart_ForViewSpecifiedWithAppRelativePath")]
public async Task PartialLayoutPaths_SpecifiedInViewStarts_GetResolvedByViewEngine(string action)
{
// Arrange
@@ -41,8 +41,8 @@ _ViewStart that specifies partial Layout
[Theory]
[InlineData("LayoutSpecifiedWithPartialPathInPage")]
[InlineData("LayoutSpecifiedWithPartialPathInPageWithPartialPath")]
+ [InlineData("LayoutSpecifiedWithPartialPathInPageWithRelativePath")]
[InlineData("LayoutSpecifiedWithPartialPathInPageWithAppRelativePath")]
- [InlineData("LayoutSpecifiedWithPartialPathInPageWithAppRelativePathWithExtension")]
public async Task PartialLayoutPaths_SpecifiedInPage_GetResolvedByViewEngine(string actionName)
{
// Arrange
@@ -58,8 +58,8 @@ _ViewStart that specifies partial Layout
}
[Theory]
- [InlineData("LayoutSpecifiedWithNonPartialPath")]
- [InlineData("LayoutSpecifiedWithNonPartialPathWithExtension")]
+ [InlineData("LayoutSpecifiedWithRelativePath")]
+ [InlineData("LayoutSpecifiedWithAppRelativePath")]
public async Task NonPartialLayoutPaths_GetResolvedByViewEngine(string actionName)
{
// Arrange
@@ -76,8 +76,8 @@ _ViewStart that specifies partial Layout
[Theory]
[InlineData("ViewWithPartial_SpecifiedWithPartialName")]
- [InlineData("ViewWithPartial_SpecifiedWithAbsoluteName")]
- [InlineData("ViewWithPartial_SpecifiedWithAbsoluteNameAndExtension")]
+ [InlineData("ViewWithPartial_SpecifiedWithRelativePath")]
+ [InlineData("ViewWithPartial_SpecifiedWithAppRelativePath")]
public async Task PartialsCanBeSpecifiedWithPartialPath(string actionName)
{
// Arrange
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/ViewEngineTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/ViewEngineTests.cs
index 9b85bf697e..c0a10209fe 100644
--- a/test/Microsoft.AspNet.Mvc.FunctionalTests/ViewEngineTests.cs
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/ViewEngineTests.cs
@@ -326,6 +326,30 @@ Page Content
Assert.Equal(expected, body.Trim(), ignoreLineEndingDifferences: true);
}
+ [Fact]
+ public async Task RelativePathsWorkAsExpected()
+ {
+ // Arrange
+ var expected =
+@"
+
+/ViewEngine/ViewWithRelativePath
+ViewWithRelativePath-content
+partial-content
+View with relative path title
+Component with Relative Path
+
+WriteLiteral says:Write says:98052WriteLiteral says:
+
+";
+
+ // Act
+ var body = await Client.GetStringAsync("http://localhost/ViewEngine/ViewWithRelativePath");
+
+ // Assert
+ Assert.Equal(expected, body.Trim(), ignoreLineEndingDifferences: true);
+ }
+
[Fact]
public async Task ViewComponentsDoNotExecuteViewStarts()
{
diff --git a/test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewEngineTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewEngineTest.cs
index 81265e473a..453a11628d 100644
--- a/test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewEngineTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewEngineTest.cs
@@ -32,7 +32,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
{"controller", "bar"},
};
- public static IEnumerable InvalidViewNameValues
+ public static IEnumerable AbsoluteViewPathData
{
get
{
@@ -80,43 +80,75 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var context = GetActionContext(_controllerTestContext);
// Act & Assert
- ExceptionAssert.ThrowsArgumentNullOrEmpty(() => viewEngine.FindView(context, viewName), "viewName");
+ ExceptionAssert.ThrowsArgumentNullOrEmpty(
+ () => viewEngine.FindView(context, viewName, isPartial: false),
+ "viewName");
}
[Theory]
- [MemberData(nameof(InvalidViewNameValues))]
- public void FindView_WithFullPathReturnsNotFound_WhenPathDoesNotMatchExtension(string viewName)
+ [MemberData(nameof(AbsoluteViewPathData))]
+ public void FindView_WithFullPath_ReturnsNotFound(string viewName)
{
// Arrange
- var viewEngine = CreateViewEngine();
+ var viewEngine = CreateSuccessfulViewEngine();
var context = GetActionContext(_controllerTestContext);
// Act
- var result = viewEngine.FindView(context, viewName);
+ var result = viewEngine.FindView(context, viewName, isPartial: false);
// Assert
Assert.False(result.Success);
}
[Theory]
- [MemberData(nameof(InvalidViewNameValues))]
- public void FindViewFullPathSucceedsWithCshtmlEnding(string viewName)
+ [MemberData(nameof(AbsoluteViewPathData))]
+ public void FindView_WithFullPathAndCshtmlEnding_ReturnsNotFound(string viewName)
{
// Arrange
- var viewEngine = CreateViewEngine();
- // Append .cshtml so the viewname is no longer invalid
+ var viewEngine = CreateSuccessfulViewEngine();
+ var context = GetActionContext(_controllerTestContext);
viewName += ".cshtml";
+
+ // Act
+ var result = viewEngine.FindView(context, viewName, isPartial: false);
+
+ // Assert
+ Assert.False(result.Success);
+ }
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public void FindView_WithRelativePath_ReturnsNotFound(bool isPartial)
+ {
+ // Arrange
+ var viewEngine = CreateSuccessfulViewEngine();
var context = GetActionContext(_controllerTestContext);
- // Act & Assert
- // If this throws then our test case fails
- var result = viewEngine.FindPartialView(context, viewName);
+ // Act
+ var result = viewEngine.FindView(context, "View.cshtml", isPartial);
+ // Assert
+ Assert.False(result.Success);
+ }
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public void GetView_WithViewName_ReturnsNotFound(bool isPartial)
+ {
+ // Arrange
+ var viewEngine = CreateSuccessfulViewEngine();
+
+ // Act
+ var result = viewEngine.GetView("~/Home/View1.cshtml", "View2", isPartial);
+
+ // Assert
Assert.False(result.Success);
}
[Fact]
- public void FindPartialView_ReturnsRazorView_IfLookupWasSuccessful()
+ public void FindView_IsPartial_ReturnsRazorView_IfLookupWasSuccessful()
{
// Arrange
var pageFactory = new Mock();
@@ -140,7 +172,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var context = GetActionContext(_controllerTestContext);
// Act
- var result = viewEngine.FindPartialView(context, "test-view");
+ var result = viewEngine.FindView(context, "test-view", isPartial: true);
// Assert
Assert.True(result.Success);
@@ -151,7 +183,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
}
[Fact]
- public void FindPartialView_DoesNotExpireCachedResults_IfViewStartsExpire()
+ public void FindView_IsPartial_DoesNotExpireCachedResults_IfViewStartsExpire()
{
// Arrange
var pageFactory = new Mock();
@@ -172,7 +204,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var context = GetActionContext(_controllerTestContext);
// Act - 1
- var result1 = viewEngine.FindPartialView(context, "test-view");
+ var result1 = viewEngine.FindView(context, "test-view", isPartial: true);
// Assert - 1
Assert.True(result1.Success);
@@ -183,7 +215,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
// Act - 2
cancellationTokenSource.Cancel();
- var result2 = viewEngine.FindPartialView(context, "test-view");
+ var result2 = viewEngine.FindView(context, "test-view", isPartial: true);
// Assert - 2
Assert.True(result2.Success);
@@ -195,7 +227,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
[Theory]
[InlineData(null)]
[InlineData("")]
- public void FindPartialView_ThrowsIfViewNameIsNullOrEmpty(string partialViewName)
+ public void FindView_IsPartial_ThrowsIfViewNameIsNullOrEmpty(string partialViewName)
{
// Arrange
var viewEngine = CreateViewEngine();
@@ -203,52 +235,50 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
// Act & Assert
ExceptionAssert.ThrowsArgumentNullOrEmpty(
- () => viewEngine.FindPartialView(context, partialViewName),
- "partialViewName");
+ () => viewEngine.FindView(context, partialViewName, isPartial: true),
+ "viewName");
}
[Theory]
- [MemberData(nameof(InvalidViewNameValues))]
- public void FindPartialView_WithFullPathReturnsNotFound_WhenPathDoesNotMatchExtension(string partialViewName)
+ [MemberData(nameof(AbsoluteViewPathData))]
+ public void FindView_IsPartialWithFullPath_ReturnsNotFound(string partialViewName)
{
// Arrange
- var viewEngine = CreateViewEngine();
+ var viewEngine = CreateSuccessfulViewEngine();
var context = GetActionContext(_controllerTestContext);
// Act
- var result = viewEngine.FindPartialView(context, partialViewName);
+ var result = viewEngine.FindView(context, partialViewName, isPartial: true);
// Assert
Assert.False(result.Success);
}
[Theory]
- [MemberData(nameof(InvalidViewNameValues))]
- public void FindPartialViewFullPathSucceedsWithCshtmlEnding(string partialViewName)
+ [MemberData(nameof(AbsoluteViewPathData))]
+ public void FindView_IsPartialWithFullPathAndCshtmlEnding_ReturnsNotFound(string partialViewName)
{
// Arrange
- var viewEngine = CreateViewEngine();
- // Append .cshtml so the viewname is no longer invalid
- partialViewName += ".cshtml";
+ var viewEngine = CreateSuccessfulViewEngine();
var context = GetActionContext(_controllerTestContext);
+ partialViewName += ".cshtml";
- // Act & Assert
- // If this throws then our test case fails
- var result = viewEngine.FindPartialView(context, partialViewName);
+ // Act
+ var result = viewEngine.FindView(context, partialViewName, isPartial: true);
+ // Assert
Assert.False(result.Success);
}
[Fact]
- public void FindPartialViewFailureSearchesCorrectLocationsWithAreas()
+ public void FindView_IsPartial_FailsButSearchesCorrectLocations_WithAreas()
{
// Arrange
- var searchedLocations = new List();
var viewEngine = CreateViewEngine();
var context = GetActionContext(_areaTestContext);
// Act
- var result = viewEngine.FindPartialView(context, "partial");
+ var result = viewEngine.FindView(context, "partial", isPartial: true);
// Assert
Assert.False(result.Success);
@@ -261,14 +291,14 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
}
[Fact]
- public void FindPartialViewFailureSearchesCorrectLocationsWithoutAreas()
+ public void FindView_IsPartial_FailsButSearchesCorrectLocations_WithoutAreas()
{
// Arrange
var viewEngine = CreateViewEngine();
var context = GetActionContext(_controllerTestContext);
// Act
- var result = viewEngine.FindPartialView(context, "partialNoArea");
+ var result = viewEngine.FindView(context, "partialNoArea", isPartial: true);
// Assert
Assert.False(result.Success);
@@ -279,14 +309,14 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
}
[Fact]
- public void FindViewFailureSearchesCorrectLocationsWithAreas()
+ public void FindView_FailsButSearchesCorrectLocationsWithAreas()
{
// Arrange
var viewEngine = CreateViewEngine();
var context = GetActionContext(_areaTestContext);
// Act
- var result = viewEngine.FindView(context, "full");
+ var result = viewEngine.FindView(context, "full", isPartial: false);
// Assert
Assert.False(result.Success);
@@ -298,14 +328,14 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
}
[Fact]
- public void FindViewFailureSearchesCorrectLocationsWithoutAreas()
+ public void FindView_FailsButSearchesCorrectLocationsWithoutAreas()
{
// Arrange
var viewEngine = CreateViewEngine();
var context = GetActionContext(_controllerTestContext);
// Act
- var result = viewEngine.FindView(context, "fullNoArea");
+ var result = viewEngine.FindView(context, "fullNoArea", isPartial: false);
// Assert
Assert.False(result.Success);
@@ -340,7 +370,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var context = GetActionContext(_controllerTestContext);
// Act
- var result = viewEngine.FindView(context, "test-view");
+ var result = viewEngine.FindView(context, "test-view", isPartial: false);
// Assert
Assert.True(result.Success);
@@ -371,7 +401,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var context = GetActionContext(_controllerTestContext);
// Act
- var result = viewEngine.FindView(context, "test-view");
+ var result = viewEngine.FindView(context, "test-view", isPartial: false);
// Assert
pageFactory.Verify();
@@ -383,10 +413,12 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
public void FindView_UsesAreaViewLocationFormat_IfRouteContainsArea()
{
// Arrange
+ var viewName = "test-view2";
+ var expectedViewName = "fake-area-path/foo/bar/test-view2.rzr";
var pageFactory = new Mock();
var page = Mock.Of();
pageFactory
- .Setup(p => p.CreateFactory("fake-area-path/foo/bar/test-view2.rzr"))
+ .Setup(p => p.CreateFactory(expectedViewName))
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
.Verifiable();
var viewEngine = new TestableRazorViewEngine(
@@ -398,9 +430,147 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var context = GetActionContext(_areaTestContext);
// Act
- var result = viewEngine.FindView(context, "test-view2");
+ var result = viewEngine.FindView(context, viewName, isPartial: false);
// Assert
+ Assert.True(result.Success);
+ Assert.Equal(viewName, result.ViewName);
+ pageFactory.Verify();
+ }
+
+ [Theory]
+ [InlineData("Test-View.cshtml")]
+ [InlineData("/Home/Test-View.cshtml")]
+ public void GetView_DoesNotUseViewLocationFormat_WithRelativePath_IfRouteDoesNotContainArea(string viewName)
+ {
+ // Arrange
+ var expectedViewName = "/Home/Test-View.cshtml";
+ var pageFactory = new Mock();
+ var page = Mock.Of();
+ pageFactory
+ .Setup(p => p.CreateFactory(expectedViewName))
+ .Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
+ .Verifiable();
+ var viewEngine = new TestableRazorViewEngine(
+ pageFactory.Object,
+ GetOptionsAccessor());
+
+ // Act
+ var result = viewEngine.GetView("/Home/Page.cshtml", viewName, isPartial: false);
+
+ // Assert
+ Assert.True(result.Success);
+ Assert.Equal(viewName, result.ViewName);
+ pageFactory.Verify();
+ }
+
+ [Theory]
+ [InlineData("Test-View.cshtml")]
+ [InlineData("/Home/Test-View.cshtml")]
+ public void GetView_DoesNotUseViewLocationFormat_WithRelativePath_IfRouteContainArea(string viewName)
+ {
+ // Arrange
+ var expectedViewName = "/Home/Test-View.cshtml";
+ var pageFactory = new Mock();
+ var page = Mock.Of();
+ pageFactory
+ .Setup(p => p.CreateFactory(expectedViewName))
+ .Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
+ .Verifiable();
+ var viewEngine = new TestableRazorViewEngine(
+ pageFactory.Object,
+ GetOptionsAccessor());
+
+ // Act
+ var result = viewEngine.GetView("/Home/Page.cshtml", viewName, isPartial: false);
+
+ // Assert
+ Assert.True(result.Success);
+ Assert.Equal(viewName, result.ViewName);
+ pageFactory.Verify();
+ }
+
+ [Theory]
+ [InlineData("/Test-View.cshtml")]
+ [InlineData("~/Test-View.CSHTML")]
+ [InlineData("/Home/Test-View.CSHTML")]
+ [InlineData("~/Home/Test-View.cshtml")]
+ [InlineData("~/SHARED/TEST-VIEW.CSHTML")]
+ public void GetView_UsesGivenPath_WithAppRelativePath(string viewName)
+ {
+ // Arrange
+ var pageFactory = new Mock();
+ var page = Mock.Of();
+ pageFactory
+ .Setup(p => p.CreateFactory(viewName))
+ .Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
+ .Verifiable();
+ var viewEngine = new TestableRazorViewEngine(
+ pageFactory.Object,
+ GetOptionsAccessor());
+
+ // Act
+ var result = viewEngine.GetView(executingFilePath: null, viewPath: viewName, isPartial: false);
+
+ // Assert
+ Assert.True(result.Success);
+ Assert.Equal(viewName, result.ViewName);
+ pageFactory.Verify();
+ }
+
+ [Theory]
+ [InlineData("Test-View.cshtml")]
+ [InlineData("Test-View.CSHTML")]
+ [InlineData("PATH/TEST-VIEW.CSHTML")]
+ [InlineData("Path1/Path2/Test-View.cshtml")]
+ public void GetView_ResolvesRelativeToCurrentPage_WithRelativePath(string viewName)
+ {
+ // Arrange
+ var expectedViewName = $"/Home/{ viewName }";
+ var pageFactory = new Mock();
+ var page = Mock.Of();
+ pageFactory
+ .Setup(p => p.CreateFactory(expectedViewName))
+ .Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
+ .Verifiable();
+ var viewEngine = new TestableRazorViewEngine(
+ pageFactory.Object,
+ GetOptionsAccessor());
+
+ // Act
+ var result = viewEngine.GetView("/Home/Page.cshtml", viewName, isPartial: false);
+
+ // Assert
+ Assert.True(result.Success);
+ Assert.Equal(viewName, result.ViewName);
+ pageFactory.Verify();
+ }
+
+ [Theory]
+ [InlineData("Test-View.cshtml")]
+ [InlineData("Test-View.CSHTML")]
+ [InlineData("PATH/TEST-VIEW.CSHTML")]
+ [InlineData("Path1/Path2/Test-View.cshtml")]
+ public void GetView_ResolvesRelativeToAppRoot_WithRelativePath_IfNoPageExecuting(string viewName)
+ {
+ // Arrange
+ var expectedViewName = $"/{ viewName }";
+ var pageFactory = new Mock();
+ var page = Mock.Of();
+ pageFactory
+ .Setup(p => p.CreateFactory(expectedViewName))
+ .Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
+ .Verifiable();
+ var viewEngine = new TestableRazorViewEngine(
+ pageFactory.Object,
+ GetOptionsAccessor());
+
+ // Act
+ var result = viewEngine.GetView(executingFilePath: null, viewPath: viewName, isPartial: false);
+
+ // Assert
+ Assert.True(result.Success);
+ Assert.Equal(viewName, result.ViewName);
pageFactory.Verify();
var view = Assert.IsType(result.View);
Assert.Same(page, view.RazorPage);
@@ -459,7 +629,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var context = GetActionContext(routeValues);
// Act
- var result = viewEngine.FindView(context, "test-view");
+ var result = viewEngine.FindView(context, "test-view", isPartial: false);
// Assert
Assert.True(result.Success);
@@ -488,7 +658,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var context = GetActionContext(_controllerTestContext);
// Act 1
- var result1 = viewEngine.FindView(context, "baz");
+ var result1 = viewEngine.FindView(context, "baz", isPartial: false);
// Assert 1
Assert.True(result1.Success);
@@ -501,7 +671,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
.Setup(p => p.CreateFactory(It.IsAny()))
.Throws(new Exception("Shouldn't be called"));
- var result2 = viewEngine.FindView(context, "baz");
+ var result2 = viewEngine.FindView(context, "baz", isPartial: false);
// Assert 2
Assert.True(result2.Success);
@@ -539,7 +709,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var context = GetActionContext(_controllerTestContext);
// Act 1
- var result1 = viewEngine.FindView(context, "baz");
+ var result1 = viewEngine.FindView(context, "baz", isPartial: false);
// Assert 1
Assert.True(result1.Success);
@@ -548,7 +718,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
// Act 2
cancellationTokenSource.Cancel();
- var result2 = viewEngine.FindView(context, "baz");
+ var result2 = viewEngine.FindView(context, "baz", isPartial: false);
// Assert 2
Assert.True(result2.Success);
@@ -591,7 +761,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var context = GetActionContext(_controllerTestContext);
// Act 1
- var result1 = viewEngine.FindView(context, "baz");
+ var result1 = viewEngine.FindView(context, "baz", isPartial: false);
// Assert 1
Assert.True(result1.Success);
@@ -601,7 +771,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
// Act 2
cancellationTokenSource.Cancel();
- var result2 = viewEngine.FindView(context, "baz");
+ var result2 = viewEngine.FindView(context, "baz", isPartial: false);
// Assert 2
Assert.True(result2.Success);
@@ -646,7 +816,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var context = GetActionContext(_controllerTestContext);
// Act - 1
- var result = viewEngine.FindView(context, "myview");
+ var result = viewEngine.FindView(context, "myview", isPartial: false);
// Assert - 1
Assert.False(result.Success);
@@ -654,7 +824,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
expander.Verify();
// Act - 2
- result = viewEngine.FindView(context, "myview");
+ result = viewEngine.FindView(context, "myview", isPartial: false);
// Assert - 2
Assert.False(result.Success);
@@ -705,7 +875,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var context = GetActionContext(_controllerTestContext);
// Act - 1
- var result = viewEngine.FindView(context, "MyView");
+ var result = viewEngine.FindView(context, "MyView", isPartial: false);
// Assert - 1
Assert.False(result.Success);
@@ -717,7 +887,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
.Setup(p => p.CreateFactory("viewlocation3"))
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]));
cancellationTokenSource.Cancel();
- result = viewEngine.FindView(context, "MyView");
+ result = viewEngine.FindView(context, "MyView", isPartial: false);
// Assert - 2
Assert.True(result.Success);
@@ -731,23 +901,86 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
Times.Exactly(2));
}
+ [Theory]
+ [MemberData(nameof(AbsoluteViewPathData))]
+ public void FindPage_WithFullPath_ReturnsNotFound(string viewName)
+ {
+ // Arrange
+ var viewEngine = CreateSuccessfulViewEngine();
+ var context = GetActionContext(_controllerTestContext);
+
+ // Act
+ var result = viewEngine.FindPage(context, viewName, isPartial: false);
+
+ // Assert
+ Assert.Null(result.Page);
+ }
+
+ [Theory]
+ [MemberData(nameof(AbsoluteViewPathData))]
+ public void FindPage_WithFullPathAndCshtmlEnding_ReturnsNotFound(string viewName)
+ {
+ // Arrange
+ var viewEngine = CreateSuccessfulViewEngine();
+ var context = GetActionContext(_controllerTestContext);
+ viewName += ".cshtml";
+
+ // Act
+ var result = viewEngine.FindPage(context, viewName, isPartial: false);
+
+ // Assert
+ Assert.Null(result.Page);
+ }
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public void FindPage_WithRelativePath_ReturnsNotFound(bool isPartial)
+ {
+ // Arrange
+ var viewEngine = CreateSuccessfulViewEngine();
+ var context = GetActionContext(_controllerTestContext);
+
+ // Act
+ var result = viewEngine.FindPage(context, "View.cshtml", isPartial);
+
+ // Assert
+ Assert.Null(result.Page);
+ }
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public void GetPage_WithViewName_ReturnsNotFound(bool isPartial)
+ {
+ // Arrange
+ var viewEngine = CreateSuccessfulViewEngine();
+
+ // Act
+ var result = viewEngine.GetPage("~/Home/View1.cshtml", "View2", isPartial);
+
+ // Assert
+ Assert.Null(result.Page);
+ }
+
[Theory]
[InlineData(null)]
[InlineData("")]
- public void FindPage_ThrowsIfNameIsNullOrEmpty(string pageName)
+ public void FindPage_IsPartial_ThrowsIfNameIsNullOrEmpty(string pageName)
{
// Arrange
var viewEngine = CreateViewEngine();
var context = GetActionContext(_controllerTestContext);
// Act & Assert
- ExceptionAssert.ThrowsArgumentNullOrEmpty(() => viewEngine.FindPage(context, pageName),
- "pageName");
+ ExceptionAssert.ThrowsArgumentNullOrEmpty(
+ () => viewEngine.FindPage(context, pageName, isPartial: true),
+ "pageName");
}
[Theory]
[MemberData(nameof(ViewLocationExpanderTestData))]
- public void FindPage_UsesViewLocationExpander_ToExpandPaths(
+ public void FindPage_IsPartial_UsesViewLocationExpander_ToExpandPaths(
IDictionary routeValues,
IEnumerable expectedSeeds)
{
@@ -789,7 +1022,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var context = GetActionContext(routeValues);
// Act
- var result = viewEngine.FindPage(context, "layout");
+ var result = viewEngine.FindPage(context, "layout", isPartial: true);
// Assert
Assert.Equal("layout", result.Name);
@@ -799,8 +1032,10 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
expander.Verify();
}
- [Fact]
- public void FindPage_ReturnsSearchedLocationsIfPageCannotBeFound()
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public void FindPage_ReturnsSearchedLocationsIfPageCannotBeFound(bool isPartial)
{
// Arrange
var expected = new[]
@@ -808,13 +1043,12 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
"/Views/bar/layout.cshtml",
"/Views/Shared/layout.cshtml",
};
- var page = Mock.Of();
var viewEngine = CreateViewEngine();
var context = GetActionContext(_controllerTestContext);
// Act
- var result = viewEngine.FindPage(context, "layout");
+ var result = viewEngine.FindPage(context, "layout", isPartial);
// Assert
Assert.Equal("layout", result.Name);
@@ -827,7 +1061,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
[InlineData(true)]
// Looks in RouteConstraints
[InlineData(false)]
- public void FindPage_SelectsActionCaseInsensitively(bool isAttributeRouted)
+ public void FindPage_IsPartial_SelectsActionCaseInsensitively(bool isAttributeRouted)
{
// The ActionDescriptor contains "Foo" and the RouteData contains "foo"
// which matches the case of the constructor thus searching in the appropriate location.
@@ -836,12 +1070,13 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
{
{ "controller", "foo" }
};
+ var page = new Mock(MockBehavior.Strict);
+ page.SetupSet(p => p.IsPartial = true);
- var page = new Mock(MockBehavior.Strict).Object;
var pageFactory = new Mock();
pageFactory
.Setup(p => p.CreateFactory("/Views/Foo/details.cshtml"))
- .Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
+ .Returns(new RazorPageFactoryResult(() => page.Object, new IChangeToken[0]))
.Verifiable();
var viewEngine = CreateViewEngine(pageFactory.Object);
@@ -850,14 +1085,17 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
{ "controller", "Foo" }
};
- var context = GetActionContextWithActionDescriptor(routeValues, routesInActionDescriptor, isAttributeRouted);
+ var context = GetActionContextWithActionDescriptor(
+ routeValues,
+ routesInActionDescriptor,
+ isAttributeRouted);
// Act
- var result = viewEngine.FindPage(context, "details");
+ var result = viewEngine.FindPage(context, "details", isPartial: true);
// Assert
Assert.Equal("details", result.Name);
- Assert.Same(page, result.Page);
+ Assert.Same(page.Object, result.Page);
Assert.Null(result.SearchedLocations);
pageFactory.Verify();
}
@@ -867,7 +1105,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
[InlineData(true)]
// Looks in RouteConstraints
[InlineData(false)]
- public void FindPage_LooksForPages_UsingActionDescriptor_Controller(bool isAttributeRouted)
+ public void FindPage_IsPartial_LooksForPages_UsingActionDescriptor_Controller(bool isAttributeRouted)
{
// Arrange
var expected = new[]
@@ -884,13 +1122,15 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
{
{ "controller", "bar" }
};
- var page = Mock.Of();
var viewEngine = CreateViewEngine();
- var context = GetActionContextWithActionDescriptor(routeValues, routesInActionDescriptor, isAttributeRouted);
+ var context = GetActionContextWithActionDescriptor(
+ routeValues,
+ routesInActionDescriptor,
+ isAttributeRouted);
// Act
- var result = viewEngine.FindPage(context, "foo");
+ var result = viewEngine.FindPage(context, "foo", isPartial: true);
// Assert
Assert.Equal("foo", result.Name);
@@ -903,7 +1143,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
[InlineData(true)]
// Looks in RouteConstraints
[InlineData(false)]
- public void FindPage_LooksForPages_UsingActionDescriptor_Areas(bool isAttributeRouted)
+ public void FindPage_IsPartial_LooksForPages_UsingActionDescriptor_Areas(bool isAttributeRouted)
{
// Arrange
var expected = new[]
@@ -923,13 +1163,15 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
{ "controller", "bar" },
{ "area", "world" }
};
- var page = Mock.Of();
var viewEngine = CreateViewEngine();
- var context = GetActionContextWithActionDescriptor(routeValues, routesInActionDescriptor, isAttributeRouted);
+ var context = GetActionContextWithActionDescriptor(
+ routeValues,
+ routesInActionDescriptor,
+ isAttributeRouted);
// Act
- var result = viewEngine.FindPage(context, "foo");
+ var result = viewEngine.FindPage(context, "foo", isPartial: true);
// Assert
Assert.Equal("foo", result.Name);
@@ -940,7 +1182,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
[Theory]
[InlineData(true)]
[InlineData(false)]
- public void FindPage_LooksForPages_UsesRouteValuesAsFallback(bool isAttributeRouted)
+ public void FindPage_IsPartial_LooksForPages_UsesRouteValuesAsFallback(bool isAttributeRouted)
{
// Arrange
var expected = new[]
@@ -953,13 +1195,15 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
{
{ "controller", "foo" }
};
- var page = Mock.Of();
var viewEngine = CreateViewEngine();
- var context = GetActionContextWithActionDescriptor(routeValues, new Dictionary(), isAttributeRouted);
+ var context = GetActionContextWithActionDescriptor(
+ routeValues,
+ new Dictionary(),
+ isAttributeRouted);
// Act
- var result = viewEngine.FindPage(context, "bar");
+ var result = viewEngine.FindPage(context, "bar", isPartial: true);
// Assert
Assert.Equal("bar", result.Name);
@@ -967,6 +1211,166 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
Assert.Equal(expected, result.SearchedLocations);
}
+ [Theory]
+ [InlineData("/Test-View.cshtml")]
+ [InlineData("~/Test-View.CSHTML")]
+ [InlineData("/Home/Test-View.CSHTML")]
+ [InlineData("~/Home/Test-View.cshtml")]
+ [InlineData("~/SHARED/TEST-VIEW.CSHTML")]
+ public void GetPage_UsesGivenPath_WithAppRelativePath(string pageName)
+ {
+ // Arrange
+ var pageFactory = new Mock();
+ var page = Mock.Of();
+ pageFactory
+ .Setup(p => p.CreateFactory(pageName))
+ .Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
+ .Verifiable();
+ var viewEngine = new TestableRazorViewEngine(
+ pageFactory.Object,
+ GetOptionsAccessor());
+
+ // Act
+ var result = viewEngine.GetPage("~/Another/Place.cshtml", pagePath: pageName, isPartial: false);
+
+ // Assert
+ Assert.Same(page, result.Page);
+ Assert.Equal(pageName, result.Name);
+ pageFactory.Verify();
+ }
+
+ [Theory]
+ [InlineData("Test-View.cshtml")]
+ [InlineData("Test-View.CSHTML")]
+ [InlineData("PATH/TEST-VIEW.CSHTML")]
+ [InlineData("Path1/Path2/Test-View.cshtml")]
+ public void GetPage_ResolvesRelativeToCurrentPage_WithRelativePath(string pageName)
+ {
+ // Arrange
+ var expectedPageName = $"/Home/{ pageName }";
+ var pageFactory = new Mock();
+ var page = Mock.Of();
+ pageFactory
+ .Setup(p => p.CreateFactory(expectedPageName))
+ .Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
+ .Verifiable();
+ var viewEngine = new TestableRazorViewEngine(
+ pageFactory.Object,
+ GetOptionsAccessor());
+
+ // Act
+ var result = viewEngine.GetPage("/Home/Page.cshtml", pageName, isPartial: false);
+
+ // Assert
+ Assert.Same(page, result.Page);
+ Assert.Equal(pageName, result.Name);
+ pageFactory.Verify();
+ }
+
+ [Theory]
+ [InlineData("Test-View.cshtml")]
+ [InlineData("Test-View.CSHTML")]
+ [InlineData("PATH/TEST-VIEW.CSHTML")]
+ [InlineData("Path1/Path2/Test-View.cshtml")]
+ public void GetPage_ResolvesRelativeToAppRoot_WithRelativePath_IfNoPageExecuting(string pageName)
+ {
+ // Arrange
+ var expectedPageName = $"/{ pageName }";
+ var pageFactory = new Mock();
+ var page = Mock.Of();
+ pageFactory
+ .Setup(p => p.CreateFactory(expectedPageName))
+ .Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
+ .Verifiable();
+ var viewEngine = new TestableRazorViewEngine(
+ pageFactory.Object,
+ GetOptionsAccessor());
+
+ // Act
+ var result = viewEngine.GetPage(executingFilePath: null, pagePath: pageName, isPartial: false);
+
+ // Assert
+ Assert.Same(page, result.Page);
+ Assert.Equal(pageName, result.Name);
+ pageFactory.Verify();
+ }
+
+ [Theory]
+ [InlineData(null, null)]
+ [InlineData(null, "")]
+ [InlineData(null, "Page")]
+ [InlineData(null, "Folder/Page")]
+ [InlineData(null, "Folder1/Folder2/Page")]
+ [InlineData("/Home/Index.cshtml", null)]
+ [InlineData("/Home/Index.cshtml", "")]
+ [InlineData("/Home/Index.cshtml", "Page")]
+ [InlineData("/Home/Index.cshtml", "Folder/Page")]
+ [InlineData("/Home/Index.cshtml", "Folder1/Folder2/Page")]
+ public void MakePathAbsolute_ReturnsPagePathUnchanged_IfNotAPath(string executingFilePath, string pagePath)
+ {
+ // Arrange
+ var viewEngine = CreateViewEngine();
+
+ // Act
+ var result = viewEngine.MakePathAbsolute(executingFilePath, pagePath);
+
+ // Assert
+ Assert.Same(pagePath, result);
+ }
+
+ [Theory]
+ [InlineData(null, "/Page")]
+ [InlineData(null, "~/Folder/Page.cshtml")]
+ [InlineData(null, "/Folder1/Folder2/Page.rzr")]
+ [InlineData("/Home/Index.cshtml", "~/Page")]
+ [InlineData("/Home/Index.cshtml", "/Folder/Page.cshtml")]
+ [InlineData("/Home/Index.cshtml", "~/Folder1/Folder2/Page.rzr")]
+ public void MakePathAbsolute_ReturnsPageUnchanged_IfAppRelative(string executingFilePath, string pagePath)
+ {
+ // Arrange
+ var viewEngine = CreateViewEngine();
+
+ // Act
+ var result = viewEngine.MakePathAbsolute(executingFilePath, pagePath);
+
+ // Assert
+ Assert.Same(pagePath, result);
+ }
+
+ [Theory]
+ [InlineData("Page.cshtml")]
+ [InlineData("Folder/Page.cshtml")]
+ [InlineData("../../Folder1/Folder2/Page.cshtml")]
+ public void MakePathAbsolute_ResolvesRelativeToExecutingPage(string pagePath)
+ {
+ // Arrange
+ var expectedPagePath = "/Home/" + pagePath;
+ var viewEngine = CreateViewEngine();
+
+ // Act
+ var result = viewEngine.MakePathAbsolute("/Home/Page.cshtml", pagePath);
+
+ // Assert
+ Assert.Equal(expectedPagePath, result);
+ }
+
+ [Theory]
+ [InlineData("Page.cshtml")]
+ [InlineData("Folder/Page.cshtml")]
+ [InlineData("../../Folder1/Folder2/Page.cshtml")]
+ public void MakePathAbsolute_ResolvesRelativeToAppRoot_IfNoPageExecuting(string pagePath)
+ {
+ // Arrange
+ var expectedPagePath = "/" + pagePath;
+ var viewEngine = CreateViewEngine();
+
+ // Act
+ var result = viewEngine.MakePathAbsolute(executingFilePath: null, pagePath: pagePath);
+
+ // Assert
+ Assert.Equal(expectedPagePath, result);
+ }
+
[Fact]
public void AreaViewLocationFormats_ContainsExpectedLocations()
{
@@ -1245,6 +1649,17 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
}
}
+ // Return RazorViewEngine with factories that always successfully create instances.
+ private RazorViewEngine CreateSuccessfulViewEngine()
+ {
+ var pageFactory = new Mock(MockBehavior.Strict);
+ pageFactory
+ .Setup(f => f.CreateFactory(It.IsAny()))
+ .Returns(new RazorPageFactoryResult(() => Mock.Of(), new IChangeToken[0]));
+
+ return CreateViewEngine(pageFactory.Object);
+ }
+
private TestableRazorViewEngine CreateViewEngine(
IRazorPageFactoryProvider pageFactory = null,
IEnumerable expanders = null)
@@ -1268,7 +1683,8 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
}
var optionsAccessor = new Mock>();
- optionsAccessor.SetupGet(v => v.Value)
+ optionsAccessor
+ .SetupGet(v => v.Value)
.Returns(options);
return optionsAccessor.Object;
}
diff --git a/test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewTest.cs
index 4491262b9b..0f726466f9 100644
--- a/test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewTest.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Http.Internal;
@@ -79,13 +80,14 @@ namespace Microsoft.AspNet.Mvc.Razor
var viewContext = CreateViewContext(view);
var expectedWriter = viewContext.Writer;
- activator.Setup(a => a.Activate(page, It.IsAny()))
- .Callback((IRazorPage p, ViewContext c) =>
- {
- Assert.Same(c, viewContext);
- c.ViewData = viewData;
- })
- .Verifiable();
+ activator
+ .Setup(a => a.Activate(page, It.IsAny()))
+ .Callback((IRazorPage p, ViewContext c) =>
+ {
+ Assert.Same(c, viewContext);
+ c.ViewData = viewData;
+ })
+ .Verifiable();
// Act
await view.RenderAsync(viewContext);
@@ -132,8 +134,12 @@ namespace Microsoft.AspNet.Mvc.Razor
var activator = Mock.Of();
var viewEngine = new Mock();
- viewEngine.Setup(v => v.FindPage(It.IsAny(), LayoutPath))
- .Returns(new RazorPageResult(LayoutPath, layout));
+ viewEngine
+ .Setup(p => p.MakePathAbsolute("_ViewStart", LayoutPath))
+ .Returns(LayoutPath);
+ viewEngine
+ .Setup(v => v.GetPage(pagePath, LayoutPath, /*isPartial*/ true))
+ .Returns(new RazorPageResult(LayoutPath, layout));
var view = new RazorView(
viewEngine.Object,
activator,
@@ -158,8 +164,9 @@ namespace Microsoft.AspNet.Mvc.Razor
// Arrange
var page = new TestableRazorPage(v => { });
var activator = new Mock();
- activator.Setup(a => a.Activate(page, It.IsAny()))
- .Verifiable();
+ activator
+ .Setup(a => a.Activate(page, It.IsAny()))
+ .Verifiable();
var view = new RazorView(
Mock.Of(),
activator.Object,
@@ -181,9 +188,10 @@ namespace Microsoft.AspNet.Mvc.Razor
{
// Arrange
var htmlEncoder = new HtmlTestEncoder();
- var expected = string.Join(Environment.NewLine,
- "HtmlEncode[[layout-content",
- "]]HtmlEncode[[page-content]]");
+ var expected = string.Join(
+ Environment.NewLine,
+ "HtmlEncode[[layout-content",
+ "]]HtmlEncode[[page-content]]");
var page = new TestableRazorPage(v =>
{
v.HtmlEncoder = htmlEncoder;
@@ -202,9 +210,10 @@ namespace Microsoft.AspNet.Mvc.Razor
.Setup(p => p.CreateFactory(LayoutPath))
.Returns(new RazorPageFactoryResult(() => layout, new IChangeToken[0]));
- var viewEngine = new Mock();
- viewEngine.Setup(v => v.FindPage(It.IsAny(), LayoutPath))
- .Returns(new RazorPageResult(LayoutPath, layout));
+ var viewEngine = new Mock(MockBehavior.Strict);
+ viewEngine
+ .Setup(v => v.GetPage(/*executingFilePath*/ null, LayoutPath, /*isPartial*/ true))
+ .Returns(new RazorPageResult(LayoutPath, layout));
var view = new RazorView(
viewEngine.Object,
@@ -283,8 +292,9 @@ namespace Microsoft.AspNet.Mvc.Razor
v.WriteLiteral("Hello world");
});
var activator = new Mock();
- activator.Setup(a => a.Activate(page, It.IsAny()))
- .Verifiable();
+ activator
+ .Setup(a => a.Activate(page, It.IsAny()))
+ .Verifiable();
var view = new RazorView(
Mock.Of(),
activator.Object,
@@ -323,14 +333,26 @@ namespace Microsoft.AspNet.Mvc.Razor
v.Layout = null;
});
var activator = new Mock();
- activator.Setup(a => a.Activate(viewStart1, It.IsAny()))
- .Verifiable();
- activator.Setup(a => a.Activate(viewStart2, It.IsAny()))
- .Verifiable();
- activator.Setup(a => a.Activate(page, It.IsAny()))
- .Verifiable();
+ activator
+ .Setup(a => a.Activate(viewStart1, It.IsAny()))
+ .Verifiable();
+ activator
+ .Setup(a => a.Activate(viewStart2, It.IsAny()))
+ .Verifiable();
+ activator
+ .Setup(a => a.Activate(page, It.IsAny()))
+ .Verifiable();
+
+ var viewEngine = new Mock(MockBehavior.Strict);
+ viewEngine
+ .Setup(engine => engine.MakePathAbsolute(/*executingFilePath*/ null, "/fake-layout-path"))
+ .Returns("/fake-layout-path");
+ viewEngine
+ .Setup(engine => engine.MakePathAbsolute(/*executingFilePath*/ null, layoutPath))
+ .Returns(layoutPath);
+
var view = new RazorView(
- Mock.Of(),
+ viewEngine.Object,
activator.Object,
new[] { viewStart1, viewStart2 },
page,
@@ -349,11 +371,11 @@ namespace Microsoft.AspNet.Mvc.Razor
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 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 =>
@@ -361,18 +383,24 @@ namespace Microsoft.AspNet.Mvc.Razor
v.Layout = layoutPath;
});
- var viewEngine = new Mock();
+ var viewEngine = new Mock(MockBehavior.Strict);
var activator = new Mock();
- var view = new RazorView(viewEngine.Object,
- Mock.Of(),
- new IRazorPage[0],
- page,
- new HtmlTestEncoder(),
- isPartial: false);
+ var view = new RazorView(
+ viewEngine.Object,
+ Mock.Of(),
+ new IRazorPage[0],
+ page,
+ new HtmlTestEncoder(),
+ isPartial: false);
var viewContext = CreateViewContext(view);
- viewEngine.Setup(v => v.FindPage(viewContext, layoutPath))
- .Returns(new RazorPageResult(layoutPath, new[] { "path1", "path2" }))
- .Verifiable();
+ viewEngine
+ .Setup(v => v.GetPage(/*executingFilePath*/ null, layoutPath, /*isPartial*/ true))
+ .Returns(new RazorPageResult(layoutPath, Enumerable.Empty()))
+ .Verifiable();
+ viewEngine
+ .Setup(v => v.FindPage(viewContext, layoutPath, /*isPartial*/ true))
+ .Returns(new RazorPageResult(layoutPath, new[] { "path1", "path2" }))
+ .Verifiable();
// Act
var ex = await Assert.ThrowsAsync(() => view.RenderAsync(viewContext));
@@ -421,22 +449,26 @@ namespace Microsoft.AspNet.Mvc.Razor
v.Write(v.RenderSection("foot"));
});
var activator = new Mock();
- activator.Setup(a => a.Activate(page, It.IsAny()))
- .Verifiable();
- activator.Setup(a => a.Activate(layout, It.IsAny()))
- .Verifiable();
- var viewEngine = new Mock();
+ activator
+ .Setup(a => a.Activate(page, It.IsAny()))
+ .Verifiable();
+ activator
+ .Setup(a => a.Activate(layout, It.IsAny()))
+ .Verifiable();
+ var viewEngine = new Mock(MockBehavior.Strict);
+ viewEngine
+ .Setup(v => v.GetPage(/*executingFilePath*/ null, LayoutPath, /*isPartial*/ true))
+ .Returns(new RazorPageResult(LayoutPath, layout))
+ .Verifiable();
- var view = new RazorView(viewEngine.Object,
- activator.Object,
- new IRazorPage[0],
- page,
- new HtmlTestEncoder(),
- isPartial: false);
+ var view = new RazorView(
+ viewEngine.Object,
+ activator.Object,
+ new IRazorPage[0],
+ page,
+ new HtmlTestEncoder(),
+ isPartial: false);
var viewContext = CreateViewContext(view);
- viewEngine.Setup(p => p.FindPage(viewContext, LayoutPath))
- .Returns(new RazorPageResult(LayoutPath, layout))
- .Verifiable();
// Act
await view.RenderAsync(viewContext);
@@ -465,16 +497,18 @@ namespace Microsoft.AspNet.Mvc.Razor
{
Path = LayoutPath
};
- var viewEngine = new Mock();
- viewEngine.Setup(p => p.FindPage(It.IsAny(), LayoutPath))
- .Returns(new RazorPageResult(LayoutPath, layout));
+ var viewEngine = new Mock(MockBehavior.Strict);
+ viewEngine
+ .Setup(v => v.GetPage(/*executingFilePath*/ null, LayoutPath, /*isPartial*/ true))
+ .Returns(new RazorPageResult(LayoutPath, layout));
- var view = new RazorView(viewEngine.Object,
- Mock.Of(),
- new IRazorPage[0],
- page,
- new HtmlTestEncoder(),
- isPartial: false);
+ var view = new RazorView(
+ viewEngine.Object,
+ Mock.Of(),
+ new IRazorPage[0],
+ page,
+ new HtmlTestEncoder(),
+ isPartial: false);
var viewContext = CreateViewContext(view);
// Act and Assert
@@ -526,18 +560,21 @@ namespace Microsoft.AspNet.Mvc.Razor
Path = "/Shared/Layout2.cshtml"
};
- var viewEngine = new Mock();
- viewEngine.Setup(p => p.FindPage(It.IsAny(), "~/Shared/Layout1.cshtml"))
- .Returns(new RazorPageResult("~/Shared/Layout1.cshtml", nestedLayout));
- viewEngine.Setup(p => p.FindPage(It.IsAny(), "~/Shared/Layout2.cshtml"))
- .Returns(new RazorPageResult("~/Shared/Layout2.cshtml", baseLayout));
+ var viewEngine = new Mock(MockBehavior.Strict);
+ viewEngine
+ .Setup(v => v.GetPage(/*executingFilePath*/ null, "~/Shared/Layout1.cshtml", /*isPartial*/ true))
+ .Returns(new RazorPageResult("~/Shared/Layout1.cshtml", nestedLayout));
+ viewEngine
+ .Setup(v => v.GetPage("/Shared/Layout1.cshtml", "~/Shared/Layout2.cshtml", /*isPartial*/ true))
+ .Returns(new RazorPageResult("~/Shared/Layout2.cshtml", baseLayout));
- var view = new RazorView(viewEngine.Object,
- Mock.Of(),
- new IRazorPage[0],
- page,
- new HtmlTestEncoder(),
- isPartial: false);
+ var view = new RazorView(
+ viewEngine.Object,
+ Mock.Of(),
+ new IRazorPage[0],
+ page,
+ new HtmlTestEncoder(),
+ isPartial: false);
var viewContext = CreateViewContext(view);
// Act
@@ -586,18 +623,27 @@ namespace Microsoft.AspNet.Mvc.Razor
});
baseLayout.Path = "Layout";
- var viewEngine = new Mock();
- viewEngine.Setup(p => p.FindPage(It.IsAny(), "NestedLayout"))
- .Returns(new RazorPageResult("NestedLayout", nestedLayout));
- viewEngine.Setup(p => p.FindPage(It.IsAny(), "Layout"))
- .Returns(new RazorPageResult("Layout", baseLayout));
+ var viewEngine = new Mock(MockBehavior.Strict);
+ viewEngine
+ .Setup(v => v.GetPage(/*executingFilePath*/ null, "NestedLayout", /*isPartial*/ true))
+ .Returns(new RazorPageResult("NestedLayout", Enumerable.Empty()));
+ viewEngine
+ .Setup(p => p.FindPage(It.IsAny(), "NestedLayout", /*isPartial*/ true))
+ .Returns(new RazorPageResult("NestedLayout", nestedLayout));
+ viewEngine
+ .Setup(v => v.GetPage("NestedLayout", "Layout", /*isPartial*/ true))
+ .Returns(new RazorPageResult("Layout", Enumerable.Empty()));
+ viewEngine
+ .Setup(p => p.FindPage(It.IsAny(), "Layout", /*isPartial*/ true))
+ .Returns(new RazorPageResult("Layout", baseLayout));
- var view = new RazorView(viewEngine.Object,
- Mock.Of(),
- new IRazorPage[0],
- page,
- new HtmlTestEncoder(),
- isPartial: false);
+ var view = new RazorView(
+ viewEngine.Object,
+ Mock.Of(),
+ new IRazorPage[0],
+ page,
+ new HtmlTestEncoder(),
+ isPartial: false);
var viewContext = CreateViewContext(view);
// Act
@@ -646,18 +692,21 @@ namespace Microsoft.AspNet.Mvc.Razor
Path = "/Shared/Layout2.cshtml"
};
- var viewEngine = new Mock();
- viewEngine.Setup(p => p.FindPage(It.IsAny(), "~/Shared/Layout1.cshtml"))
- .Returns(new RazorPageResult("~/Shared/Layout1.cshtml", nestedLayout));
- viewEngine.Setup(p => p.FindPage(It.IsAny(), "~/Shared/Layout2.cshtml"))
- .Returns(new RazorPageResult("~/Shared/Layout2.cshtml", baseLayout));
+ var viewEngine = new Mock(MockBehavior.Strict);
+ viewEngine
+ .Setup(v => v.GetPage(/*executingFilePath*/ null, "~/Shared/Layout1.cshtml", /*isPartial*/ true))
+ .Returns(new RazorPageResult("~/Shared/Layout1.cshtml", nestedLayout));
+ viewEngine
+ .Setup(v => v.GetPage("/Shared/Layout1.cshtml", "~/Shared/Layout2.cshtml", /*isPartial*/ true))
+ .Returns(new RazorPageResult("~/Shared/Layout2.cshtml", baseLayout));
- var view = new RazorView(viewEngine.Object,
- Mock.Of(),
- new IRazorPage[0],
- page,
- new HtmlTestEncoder(),
- isPartial: false);
+ var view = new RazorView(
+ viewEngine.Object,
+ Mock.Of(),
+ new IRazorPage[0],
+ page,
+ new HtmlTestEncoder(),
+ isPartial: false);
var viewContext = CreateViewContext(view);
// Act and Assert
@@ -711,18 +760,21 @@ namespace Microsoft.AspNet.Mvc.Razor
Path = "/Shared/Layout2.cshtml"
};
- var viewEngine = new Mock();
- viewEngine.Setup(p => p.FindPage(It.IsAny(), "~/Shared/Layout1.cshtml"))
- .Returns(new RazorPageResult("~/Shared/Layout1.cshtml", nestedLayout));
- viewEngine.Setup(p => p.FindPage(It.IsAny(), "~/Shared/Layout2.cshtml"))
- .Returns(new RazorPageResult("~/Shared/Layout2.cshtml", baseLayout));
+ var viewEngine = new Mock(MockBehavior.Strict);
+ viewEngine
+ .Setup(p => p.GetPage("Page", "~/Shared/Layout1.cshtml", /*isPartial*/ true))
+ .Returns(new RazorPageResult("~/Shared/Layout1.cshtml", nestedLayout));
+ viewEngine
+ .Setup(p => p.GetPage("/Shared/Layout1.cshtml", "~/Shared/Layout2.cshtml", /*isPartial*/ true))
+ .Returns(new RazorPageResult("~/Shared/Layout2.cshtml", baseLayout));
- var view = new RazorView(viewEngine.Object,
- Mock.Of(),
- new IRazorPage[0],
- page,
- new HtmlTestEncoder(),
- isPartial: false);
+ var view = new RazorView(
+ viewEngine.Object,
+ Mock.Of(),
+ new IRazorPage[0],
+ page,
+ new HtmlTestEncoder(),
+ isPartial: false);
var viewContext = CreateViewContext(view);
// Act and Assert
@@ -745,16 +797,18 @@ namespace Microsoft.AspNet.Mvc.Razor
{
Path = LayoutPath
};
- var viewEngine = new Mock();
- viewEngine.Setup(p => p.FindPage(It.IsAny(), LayoutPath))
- .Returns(new RazorPageResult(LayoutPath, layout));
+ var viewEngine = new Mock(MockBehavior.Strict);
+ viewEngine
+ .Setup(p => p.GetPage(/*executingFilePath*/ null, LayoutPath, /*isPartial*/ true))
+ .Returns(new RazorPageResult(LayoutPath, layout));
- var view = new RazorView(viewEngine.Object,
- Mock.Of(),
- new IRazorPage[0],
- page,
- new HtmlTestEncoder(),
- isPartial: false);
+ var view = new RazorView(
+ viewEngine.Object,
+ Mock.Of(),
+ new IRazorPage[0],
+ page,
+ new HtmlTestEncoder(),
+ isPartial: false);
var viewContext = CreateViewContext(view);
// Act and Assert
@@ -807,18 +861,95 @@ namespace Microsoft.AspNet.Mvc.Razor
});
layout2.Path = "~/Shared/Layout2.cshtml";
- var viewEngine = new Mock();
- viewEngine.Setup(p => p.FindPage(It.IsAny(), "~/Shared/Layout1.cshtml"))
- .Returns(new RazorPageResult("~/Shared/Layout1.cshtml", layout1));
- viewEngine.Setup(p => p.FindPage(It.IsAny(), "~/Shared/Layout2.cshtml"))
- .Returns(new RazorPageResult("~/Shared/Layout2.cshtml", layout2));
+ var viewEngine = new Mock(MockBehavior.Strict);
+ viewEngine
+ .Setup(p => p.GetPage(/*executingFilePath*/ null, "~/Shared/Layout1.cshtml", /*isPartial*/ true))
+ .Returns(new RazorPageResult("~/Shared/Layout1.cshtml", layout1));
+ viewEngine
+ .Setup(p => p.GetPage("~/Shared/Layout1.cshtml", "~/Shared/Layout2.cshtml", /*isPartial*/ true))
+ .Returns(new RazorPageResult("~/Shared/Layout2.cshtml", layout2));
- var view = new RazorView(viewEngine.Object,
- Mock.Of(),
- new IRazorPage[0],
- page,
- new HtmlTestEncoder(),
- isPartial: false);
+ var view = new RazorView(
+ viewEngine.Object,
+ Mock.Of(),
+ new IRazorPage[0],
+ page,
+ new HtmlTestEncoder(),
+ isPartial: false);
+ var viewContext = CreateViewContext(view);
+
+ // Act
+ await view.RenderAsync(viewContext);
+
+ // Assert
+ Assert.Equal(expected, viewContext.Writer.ToString());
+ }
+
+ [Fact]
+ public async Task RenderAsync_ExecutesNestedLayoutPages_WithRelativePaths()
+ {
+ // Arrange
+ var htmlEncoder = new HtmlTestEncoder();
+ var expected =
+ "HtmlEncode[[layout-2" + Environment.NewLine +
+ "]]bar-content" + Environment.NewLine +
+ "HtmlEncode[[layout-1" + Environment.NewLine +
+ "]]foo-content" + Environment.NewLine +
+ "body-content";
+
+ var page = new TestableRazorPage(v =>
+ {
+ v.HtmlEncoder = htmlEncoder;
+ v.DefineSection("foo", async writer =>
+ {
+ await writer.WriteLineAsync("foo-content");
+ });
+ v.Layout = "Layout1.cshtml";
+ v.WriteLiteral("body-content");
+ })
+ {
+ Path = "~/Shared/Page.cshtml",
+ };
+
+ var layout1 = new TestableRazorPage(v =>
+ {
+ v.HtmlEncoder = htmlEncoder;
+ v.Write("layout-1" + Environment.NewLine);
+ v.Write(v.RenderSection("foo"));
+ v.DefineSection("bar", writer => writer.WriteLineAsync("bar-content"));
+ v.RenderBodyPublic();
+ v.Layout = "Layout2.cshtml";
+ })
+ {
+ Path = "~/Shared/Layout1.cshtml",
+ };
+
+ var layout2 = new TestableRazorPage(v =>
+ {
+ v.HtmlEncoder = htmlEncoder;
+ v.Write("layout-2" + Environment.NewLine);
+ v.Write(v.RenderSection("bar"));
+ v.RenderBodyPublic();
+ })
+ {
+ Path = "~/Shared/Layout2.cshtml",
+ };
+
+ var viewEngine = new Mock(MockBehavior.Strict);
+ viewEngine
+ .Setup(p => p.GetPage("~/Shared/Page.cshtml", "Layout1.cshtml", /*isPartial*/ true))
+ .Returns(new RazorPageResult("~/Shared/Layout1.cshtml", layout1));
+ viewEngine
+ .Setup(p => p.GetPage("~/Shared/Layout1.cshtml", "Layout2.cshtml", /*isPartial*/ true))
+ .Returns(new RazorPageResult("~/Shared/Layout2.cshtml", layout2));
+
+ var view = new RazorView(
+ viewEngine.Object,
+ Mock.Of(),
+ new IRazorPage[0],
+ page,
+ new HtmlTestEncoder(),
+ isPartial: false);
var viewContext = CreateViewContext(view);
// Act
@@ -845,16 +976,21 @@ namespace Microsoft.AspNet.Mvc.Razor
});
layout.Path = "Shared/Layout.cshtml";
- var viewEngine = new Mock();
- viewEngine.Setup(p => p.FindPage(It.IsAny(), "_Layout"))
- .Returns(new RazorPageResult("_Layout", layout));
+ var viewEngine = new Mock(MockBehavior.Strict);
+ viewEngine
+ .Setup(p => p.GetPage(It.IsAny(), "_Layout", /*isPartial*/ true))
+ .Returns(new RazorPageResult("_Layout", Enumerable.Empty()));
+ viewEngine
+ .Setup(p => p.FindPage(It.IsAny(), "_Layout", /*isPartial*/ true))
+ .Returns(new RazorPageResult("_Layout", layout));
- var view = new RazorView(viewEngine.Object,
- Mock.Of(),
- new IRazorPage[0],
- page,
- new HtmlTestEncoder(),
- isPartial: false);
+ var view = new RazorView(
+ viewEngine.Object,
+ Mock.Of(),
+ new IRazorPage[0],
+ page,
+ new HtmlTestEncoder(),
+ isPartial: false);
var viewContext = CreateViewContext(view);
// Act and Assert
@@ -888,18 +1024,27 @@ namespace Microsoft.AspNet.Mvc.Razor
});
layout2.Path = "/Shared/Layout2.cshtml";
- var viewEngine = new Mock();
- viewEngine.Setup(p => p.FindPage(It.IsAny(), "_Layout"))
- .Returns(new RazorPageResult("_Layout", layout1));
- viewEngine.Setup(p => p.FindPage(It.IsAny(), "_Layout2"))
- .Returns(new RazorPageResult("_Layout2", layout2));
+ var viewEngine = new Mock(MockBehavior.Strict);
+ viewEngine
+ .Setup(p => p.GetPage(It.IsAny(), "_Layout", /*isPartial*/ true))
+ .Returns(new RazorPageResult("_Layout1", Enumerable.Empty()));
+ viewEngine
+ .Setup(p => p.FindPage(It.IsAny(), "_Layout", /*isPartial*/ true))
+ .Returns(new RazorPageResult("_Layout", layout1));
+ viewEngine
+ .Setup(p => p.GetPage("Shared/_Layout.cshtml", "_Layout2", /*isPartial*/ true))
+ .Returns(new RazorPageResult("_Layout2", Enumerable.Empty()));
+ viewEngine
+ .Setup(p => p.FindPage(It.IsAny(), "_Layout2", /*isPartial*/ true))
+ .Returns(new RazorPageResult("_Layout2", layout2));
- var view = new RazorView(viewEngine.Object,
- Mock.Of(),
- new IRazorPage[0],
- page,
- new HtmlTestEncoder(),
- isPartial: false);
+ var view = new RazorView(
+ viewEngine.Object,
+ Mock.Of(),
+ new IRazorPage[0],
+ page,
+ new HtmlTestEncoder(),
+ isPartial: false);
var viewContext = CreateViewContext(view);
// Act and Assert
@@ -944,7 +1089,7 @@ namespace Microsoft.AspNet.Mvc.Razor
await writer.WriteLineAsync(htmlEncoder.Encode(v.RenderSection("foo").ToString()));
});
});
- nestedLayout.Path = "~/Shared/Layout2.cshtml";
+ nestedLayout.Path = "~/Shared/Layout1.cshtml";
var baseLayout = new TestableRazorPage(v =>
{
@@ -953,20 +1098,23 @@ namespace Microsoft.AspNet.Mvc.Razor
v.RenderBodyPublic();
v.Write(v.RenderSection("foo"));
});
- baseLayout.Path = "~/Shared/Layout1.cshtml";
+ baseLayout.Path = "~/Shared/Layout2.cshtml";
- var viewEngine = new Mock();
- viewEngine.Setup(p => p.FindPage(It.IsAny(), "~/Shared/Layout1.cshtml"))
- .Returns(new RazorPageResult("~/Shared/Layout1.cshtml", nestedLayout));
- viewEngine.Setup(p => p.FindPage(It.IsAny(), "~/Shared/Layout2.cshtml"))
- .Returns(new RazorPageResult("~/Shared/Layout2.cshtml", baseLayout));
+ var viewEngine = new Mock(MockBehavior.Strict);
+ viewEngine
+ .Setup(p => p.GetPage(/*executingFilePath*/ null, "~/Shared/Layout1.cshtml", /*isPartial*/ true))
+ .Returns(new RazorPageResult("~/Shared/Layout1.cshtml", nestedLayout));
+ viewEngine
+ .Setup(p => p.GetPage("~/Shared/Layout1.cshtml", "~/Shared/Layout2.cshtml", /*isPartial*/ true))
+ .Returns(new RazorPageResult("~/Shared/Layout2.cshtml", baseLayout));
- var view = new RazorView(viewEngine.Object,
- Mock.Of(),
- new IRazorPage[0],
- page,
- new HtmlTestEncoder(),
- isPartial: false);
+ var view = new RazorView(
+ viewEngine.Object,
+ Mock.Of(),
+ new IRazorPage[0],
+ page,
+ new HtmlTestEncoder(),
+ isPartial: false);
var viewContext = CreateViewContext(view);
// Act
@@ -1010,16 +1158,21 @@ namespace Microsoft.AspNet.Mvc.Razor
v.Write(v.RenderSection("foo"));
});
- var viewEngine = new Mock();
- viewEngine.Setup(p => p.FindPage(It.IsAny(), "layout-1"))
- .Returns(new RazorPageResult("layout-1", layout1));
+ var viewEngine = new Mock(MockBehavior.Strict);
+ viewEngine
+ .Setup(p => p.GetPage(/*executingFilePath*/ null, "layout-1", /*isPartial*/ true))
+ .Returns(new RazorPageResult("layout-1", Enumerable.Empty()));
+ viewEngine
+ .Setup(p => p.FindPage(It.IsAny(), "layout-1", /*isPartial*/ true))
+ .Returns(new RazorPageResult("layout-1", layout1));
- var view = new RazorView(viewEngine.Object,
- Mock.Of(),
- new IRazorPage[0],
- page,
- new HtmlTestEncoder(),
- isPartial: false);
+ var view = new RazorView(
+ viewEngine.Object,
+ Mock.Of(),
+ new IRazorPage[0],
+ page,
+ new HtmlTestEncoder(),
+ isPartial: false);
var viewContext = CreateViewContext(view);
// Act
@@ -1060,16 +1213,21 @@ namespace Microsoft.AspNet.Mvc.Razor
v.Write(v.RenderSection("foo"));
});
- var viewEngine = new Mock();
- viewEngine.Setup(p => p.FindPage(It.IsAny(), "layout-1"))
- .Returns(new RazorPageResult("layout-1", layout1));
+ var viewEngine = new Mock(MockBehavior.Strict);
+ viewEngine
+ .Setup(p => p.GetPage(/*executingFilePath*/ null, "layout-1", /*isPartial*/ true))
+ .Returns(new RazorPageResult("layout-1", Enumerable.Empty()));
+ viewEngine
+ .Setup(p => p.FindPage(It.IsAny(), "layout-1", /*isPartial*/ true))
+ .Returns(new RazorPageResult("layout-1", layout1));
- var view = new RazorView(viewEngine.Object,
- Mock.Of(),
- new IRazorPage[0],
- page,
- new HtmlTestEncoder(),
- isPartial: false);
+ var view = new RazorView(
+ viewEngine.Object,
+ Mock.Of(),
+ new IRazorPage[0],
+ page,
+ new HtmlTestEncoder(),
+ isPartial: false);
var viewContext = CreateViewContext(view);
// Act
@@ -1094,12 +1252,13 @@ namespace Microsoft.AspNet.Mvc.Razor
v.WriteLiteral("after-flush");
});
- var view = new RazorView(Mock.Of(),
- Mock.Of(),
- new IRazorPage[0],
- page,
- new HtmlTestEncoder(),
- isPartial: false);
+ var view = new RazorView(
+ Mock.Of(),
+ Mock.Of(),
+ new IRazorPage[0],
+ page,
+ new HtmlTestEncoder(),
+ isPartial: false);
var viewContext = CreateViewContext(view);
// Act and Assert
@@ -1134,17 +1293,19 @@ namespace Microsoft.AspNet.Mvc.Razor
v.RenderBodyPublic();
v.Layout = "~/Shared/Layout2.cshtml";
});
- var viewEngine = new Mock();
+ var viewEngine = new Mock(MockBehavior.Strict);
var layoutPath = "~/Shared/Layout1.cshtml";
- viewEngine.Setup(p => p.FindPage(It.IsAny(), layoutPath))
- .Returns(new RazorPageResult(layoutPath, layoutPage));
+ viewEngine
+ .Setup(p => p.GetPage("/Views/TestPath/Test.cshtml", layoutPath, /*isPartial*/ true))
+ .Returns(new RazorPageResult(layoutPath, layoutPage));
- var view = new RazorView(viewEngine.Object,
- Mock.Of(),
- new IRazorPage[0],
- page,
- new HtmlTestEncoder(),
- isPartial: false);
+ var view = new RazorView(
+ viewEngine.Object,
+ Mock.Of(),
+ new IRazorPage[0],
+ page,
+ new HtmlTestEncoder(),
+ isPartial: false);
var viewContext = CreateViewContext(view);
// Act and Assert
@@ -1162,31 +1323,34 @@ namespace Microsoft.AspNet.Mvc.Razor
var layoutExecuted = false;
var count = -1;
var feature = new Mock(MockBehavior.Strict);
- feature.Setup(f => f.DecorateWriter(It.IsAny()))
- .Returns(() =>
- {
- count++;
- if (count == 0)
- {
- return pageWriter;
- }
- else if (count == 1)
- {
- return layoutWriter;
- }
- throw new Exception();
- })
- .Verifiable();
+ feature
+ .Setup(f => f.DecorateWriter(It.IsAny()))
+ .Returns(() =>
+ {
+ count++;
+ if (count == 0)
+ {
+ return pageWriter;
+ }
+ else if (count == 1)
+ {
+ return layoutWriter;
+ }
+ throw new Exception();
+ })
+ .Verifiable();
var pageContext = Mock.Of();
- feature.Setup(f => f.GetContext("/MyPage.cshtml", pageWriter))
- .Returns(pageContext)
- .Verifiable();
+ feature
+ .Setup(f => f.GetContext("/MyPage.cshtml", pageWriter))
+ .Returns(pageContext)
+ .Verifiable();
var layoutContext = Mock.Of();
- feature.Setup(f => f.GetContext("/Layout.cshtml", layoutWriter))
- .Returns(layoutContext)
- .Verifiable();
+ feature
+ .Setup(f => f.GetContext("/Layout.cshtml", layoutWriter))
+ .Returns(layoutContext)
+ .Verifiable();
var page = new TestableRazorPage(v =>
{
@@ -1208,15 +1372,21 @@ namespace Microsoft.AspNet.Mvc.Razor
});
layout.Path = "/Layout.cshtml";
- var viewEngine = new Mock();
- viewEngine.Setup(p => p.FindPage(It.IsAny(), "Layout"))
- .Returns(new RazorPageResult("Layout", layout));
- var view = new RazorView(viewEngine.Object,
- Mock.Of(),
- new IRazorPage[0],
- page,
- new HtmlTestEncoder(),
- isPartial: false);
+ var viewEngine = new Mock(MockBehavior.Strict);
+ viewEngine
+ .Setup(p => p.GetPage("/MyPage.cshtml", "Layout", /*isPartial*/ true))
+ .Returns(new RazorPageResult("Layout", Enumerable.Empty()));
+ viewEngine
+ .Setup(p => p.FindPage(It.IsAny(), "Layout", /*isPartial*/ true))
+ .Returns(new RazorPageResult("/Layout.cshtml", layout));
+
+ var view = new RazorView(
+ viewEngine.Object,
+ Mock.Of(),
+ new IRazorPage[0],
+ page,
+ new HtmlTestEncoder(),
+ isPartial: false);
var viewContext = CreateViewContext(view);
viewContext.HttpContext.Features.Set(feature.Object);
@@ -1256,12 +1426,13 @@ namespace Microsoft.AspNet.Mvc.Razor
});
page.Path = "/MyPartialPage.cshtml";
- var view = new RazorView(Mock.Of(),
- Mock.Of(),
- new IRazorPage[0],
- page,
- new HtmlTestEncoder(),
- isPartial: true);
+ var view = new RazorView(
+ Mock.Of(),
+ Mock.Of(),
+ new IRazorPage[0],
+ page,
+ new HtmlTestEncoder(),
+ isPartial: true);
var viewContext = CreateViewContext(view);
viewContext.Writer = writer;
viewContext.HttpContext.Features.Set(feature.Object);
@@ -1288,12 +1459,13 @@ namespace Microsoft.AspNet.Mvc.Razor
executed = true;
});
- var view = new RazorView(Mock.Of(),
- Mock.Of(),
- new IRazorPage[0],
- page,
- new HtmlTestEncoder(),
- isPartial);
+ var view = new RazorView
+ (Mock.Of(),
+ Mock.Of(),
+ new IRazorPage[0],
+ page,
+ new HtmlTestEncoder(),
+ isPartial);
var viewContext = CreateViewContext(view);
// Act
@@ -1326,14 +1498,79 @@ namespace Microsoft.AspNet.Mvc.Razor
actualViewStart = v.Layout;
v.Layout = expectedPage;
});
- var viewEngine = Mock.Of();
+ var viewEngine = new Mock(MockBehavior.Strict);
+ viewEngine
+ .Setup(engine => engine.MakePathAbsolute(/*executingFilePath*/ null, expectedViewStart))
+ .Returns(expectedViewStart);
+ viewEngine
+ .Setup(engine => engine.MakePathAbsolute(/*executingFilePath*/ null, expectedPage))
+ .Returns(expectedPage);
- var view = new RazorView(viewEngine,
- Mock.Of(),
- new[] { viewStart1, viewStart2 },
- page,
- new HtmlTestEncoder(),
- isPartial: false);
+ var view = new RazorView(
+ viewEngine.Object,
+ Mock.Of(),
+ new[] { viewStart1, viewStart2 },
+ page,
+ new HtmlTestEncoder(),
+ isPartial: false);
+ var viewContext = CreateViewContext(view);
+
+ // Act
+ await view.RenderAsync(viewContext);
+
+ // Assert
+ Assert.Equal(expectedViewStart, actualViewStart);
+ Assert.Equal(expectedPage, actualPage);
+ }
+
+ [Fact]
+ public async Task RenderAsync_CopiesLayoutPropertyFromViewStart_WithRelativePaths()
+ {
+ // Arrange
+ var expectedViewStart = "~/_Layout.cshtml";
+ var expectedPage = "~/Home/_Layout.cshtml";
+ string actualViewStart = null;
+ string actualPage = null;
+ var page = new TestableRazorPage(v =>
+ {
+ actualPage = v.Layout;
+
+ // Clear it out because we don't care about rendering the layout in this test.
+ v.Layout = null;
+ });
+
+ var viewStart1 = new TestableRazorPage(v =>
+ {
+ v.Layout = "_Layout.cshtml";
+ })
+ {
+ Path = "~/_ViewStart.cshtml",
+ };
+
+ var viewStart2 = new TestableRazorPage(v =>
+ {
+ actualViewStart = v.Layout;
+ v.Layout = "_Layout.cshtml";
+ })
+ {
+ Path = "~/Home/_ViewStart.cshtml",
+ };
+
+ var viewEngine = new Mock(MockBehavior.Strict);
+ viewEngine
+ .Setup(engine => engine.MakePathAbsolute("~/_ViewStart.cshtml", "_Layout.cshtml"))
+ .Returns("~/_Layout.cshtml");
+ viewEngine
+ .Setup(engine => engine.MakePathAbsolute("~/Home/_ViewStart.cshtml", "_Layout.cshtml"))
+ .Returns("~/Home/_Layout.cshtml");
+
+ var view = new RazorView(
+ viewEngine.Object,
+ Mock.Of(),
+ new[] { viewStart1, viewStart2 },
+ page,
+ new HtmlTestEncoder(),
+ isPartial: false);
var viewContext = CreateViewContext(view);
// Act
@@ -1364,10 +1601,16 @@ namespace Microsoft.AspNet.Mvc.Razor
actual = v.Layout;
v.Layout = null;
});
- var viewEngine = Mock.Of();
+ var viewEngine = new Mock(MockBehavior.Strict);
+ viewEngine
+ .Setup(engine => engine.MakePathAbsolute(/*executingFilePath*/ null, "Layout"))
+ .Returns("Layout");
+ viewEngine
+ .Setup(engine => engine.MakePathAbsolute(/*executingFilePath*/ null, /*pagePath*/ null))
+ .Returns(null);
var view = new RazorView(
- viewEngine,
+ viewEngine.Object,
Mock.Of(),
new[] { viewStart1, viewStart2 },
page,
@@ -1404,10 +1647,13 @@ namespace Microsoft.AspNet.Mvc.Razor
isPartialLayout = v.IsPartial;
v.RenderBodyPublic();
});
- var viewEngine = new Mock();
+ var viewEngine = new Mock(MockBehavior.Strict);
viewEngine
- .Setup(p => p.FindPage(It.IsAny(), "/Layout.cshtml"))
- .Returns(new RazorPageResult("Layout", layout));
+ .Setup(p => p.MakePathAbsolute(/*executingFilePath*/ null, "/Layout.cshtml"))
+ .Returns("/Layout.cshtml");
+ viewEngine
+ .Setup(p => p.GetPage(/*executingFilePath*/ null, "/Layout.cshtml", /*isPartial*/ true))
+ .Returns(new RazorPageResult("/Layout.cshtml", layout));
var view = new RazorView(
viewEngine.Object,
@@ -1436,12 +1682,13 @@ namespace Microsoft.AspNet.Mvc.Razor
{
isPartialPage = v.IsPartial;
});
- var view = new RazorView(Mock.Of(),
- Mock.Of(),
- new IRazorPage[0],
- page,
- new HtmlTestEncoder(),
- isPartial: true);
+ var view = new RazorView(
+ Mock.Of(),
+ Mock.Of(),
+ new IRazorPage[0],
+ page,
+ new HtmlTestEncoder(),
+ isPartial: true);
var viewContext = CreateViewContext(view);
// Act
diff --git a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/PartialViewResultTest.cs b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/PartialViewResultTest.cs
index a3c1fe5d96..84420f6358 100644
--- a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/PartialViewResultTest.cs
+++ b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/PartialViewResultTest.cs
@@ -4,6 +4,7 @@
#if MOCK_SUPPORT
using System;
using System.Diagnostics;
+using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Internal;
@@ -36,9 +37,13 @@ namespace Microsoft.AspNet.Mvc
var actionContext = GetActionContext();
- var viewEngine = new Mock();
+ var viewEngine = new Mock(MockBehavior.Strict);
viewEngine
- .Setup(v => v.FindPartialView(It.IsAny(), It.IsAny()))
+ .Setup(v => v.GetView(/*executingFilePath*/ null, "MyView", /*isPartial*/ true))
+ .Returns(ViewEngineResult.NotFound("MyView", Enumerable.Empty()))
+ .Verifiable();
+ viewEngine
+ .Setup(v => v.FindView(It.IsAny(), "MyView", /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound("MyView", new[] { "Location1", "Location2" }))
.Verifiable();
@@ -82,7 +87,11 @@ namespace Microsoft.AspNet.Mvc
var viewEngine = new Mock(MockBehavior.Strict);
viewEngine
- .Setup(e => e.FindPartialView(context, "myview"))
+ .Setup(v => v.GetView(/*executingFilePath*/ null, "myview", /*isPartial*/ true))
+ .Returns(ViewEngineResult.NotFound("myview", Enumerable.Empty()))
+ .Verifiable();
+ viewEngine
+ .Setup(v => v.FindView(It.IsAny(), "myview", /*isPartial*/ true))
.Returns(ViewEngineResult.Found("myview", view.Object))
.Verifiable();
diff --git a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/DefaultTemplatesUtilities.cs b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/DefaultTemplatesUtilities.cs
index f8f449641b..f79d2c9426 100644
--- a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/DefaultTemplatesUtilities.cs
+++ b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/DefaultTemplatesUtilities.cs
@@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.IO;
+using System.Linq;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNet.Antiforgery;
@@ -315,10 +316,15 @@ namespace Microsoft.AspNet.Mvc.Rendering
})
.Returns(Task.FromResult(0));
- var viewEngine = new Mock();
+ var viewEngine = new Mock(MockBehavior.Strict);
viewEngine
- .Setup(v => v.FindPartialView(It.IsAny(), It.IsAny()))
- .Returns(ViewEngineResult.Found("MyView", view.Object));
+ .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny(), /*isPartial*/ true))
+ .Returns(ViewEngineResult.NotFound("MyView", Enumerable.Empty()))
+ .Verifiable();
+ viewEngine
+ .Setup(v => v.FindView(It.IsAny(), It.IsAny(), /*isPartial*/ true))
+ .Returns(ViewEngineResult.Found("MyView", view.Object))
+ .Verifiable();
return viewEngine.Object;
}
diff --git a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/ViewContextTests.cs b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/ViewContextTests.cs
index 40c37b12eb..509c819a2d 100644
--- a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/ViewContextTests.cs
+++ b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/ViewContextTests.cs
@@ -3,6 +3,7 @@
#if MOCK_SUPPORT
using System.IO;
+using System.Text;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Abstractions;
using Microsoft.AspNet.Mvc.ModelBinding;
@@ -42,6 +43,42 @@ namespace Microsoft.AspNet.Mvc.Rendering
Assert.Equal("property", context.ViewBag.Another);
Assert.Equal("property", context.ViewData["Another"]);
}
+
+ [Fact]
+ public void CopyConstructor_CopiesExpectedProperties()
+ {
+ // Arrange
+ var originalContext = new ViewContext(
+ new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor()),
+ view: Mock.Of(),
+ viewData: new ViewDataDictionary(metadataProvider: new EmptyModelMetadataProvider()),
+ tempData: new TempDataDictionary(new HttpContextAccessor(), Mock.Of()),
+ writer: TextWriter.Null,
+ htmlHelperOptions: new HtmlHelperOptions());
+ var view = Mock.Of();
+ var viewData = new ViewDataDictionary(originalContext.ViewData);
+ var writer = new StringCollectionTextWriter(Encoding.UTF8);
+
+ // Act
+ var context = new ViewContext(originalContext, view, viewData, writer);
+
+ // Assert
+ Assert.Same(originalContext.ActionDescriptor, context.ActionDescriptor);
+ Assert.Equal(originalContext.ClientValidationEnabled, context.ClientValidationEnabled);
+ Assert.Same(originalContext.ExecutingFilePath, context.ExecutingFilePath);
+ Assert.Same(originalContext.FormContext, context.FormContext);
+ Assert.Equal(originalContext.Html5DateRenderingMode, context.Html5DateRenderingMode);
+ Assert.Same(originalContext.HttpContext, context.HttpContext);
+ Assert.Same(originalContext.ModelState, context.ModelState);
+ Assert.Same(originalContext.RouteData, context.RouteData);
+ Assert.Same(originalContext.TempData, context.TempData);
+ Assert.Same(originalContext.ValidationMessageElement, context.ValidationMessageElement);
+ Assert.Same(originalContext.ValidationSummaryMessageElement, context.ValidationSummaryMessageElement);
+
+ Assert.Same(view, context.View);
+ Assert.Same(viewData, context.ViewData);
+ Assert.Same(writer, context.Writer);
+ }
}
}
#endif
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewComponents/ViewViewComponentResultTest.cs b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewComponents/ViewViewComponentResultTest.cs
index 312c908430..95c72d64aa 100644
--- a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewComponents/ViewViewComponentResultTest.cs
+++ b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewComponents/ViewViewComponentResultTest.cs
@@ -5,6 +5,7 @@
using System;
using System.Diagnostics;
using System.IO;
+using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Abstractions;
@@ -35,9 +36,14 @@ namespace Microsoft.AspNet.Mvc
.Verifiable();
var viewEngine = new Mock(MockBehavior.Strict);
- viewEngine.Setup(e => e.FindPartialView(It.IsAny(), It.IsAny()))
- .Returns(ViewEngineResult.Found("some-view", view.Object))
- .Verifiable();
+ viewEngine
+ .Setup(v => v.GetView(/*executingFilePath*/ null, "some-view", /*isPartial*/ true))
+ .Returns(ViewEngineResult.NotFound("some-view", Enumerable.Empty