Add relative view path support

- #3307
- relative paths are now supported in `View()` calls from components and view components,
  `Html.PartialAsync()` and similar calls, and `RazorPage.Layout` settings.
  - support absolute paths, relative paths, and view location lookups consistently / everywhere
  - support view paths in `TemplateRenderer` e.g. passing an absolute path to `Html.EditorFor()`
- take a big swing at the `IRazorViewEngine` and `IViewEngine` interfaces
  - split lookups (view names) from navigation (view paths)
  - remove `Partial` separation; use parameters to set `IsPartial` properties
- correct `ViewContext` copy constructor and add unit test
- extend unit tests to cover relative paths
  - fix existing tests to handle newly-required extension in an absolute path
- add functional test that chains relative paths

nits:
- remove some YOLO line wrapping
- `""` -> `string.Empty`
This commit is contained in:
Doug Bunting 2015-10-30 12:40:20 -07:00
parent 349b2f3963
commit 08dd77d8c7
39 changed files with 1963 additions and 770 deletions

View File

@ -11,12 +11,38 @@ namespace Microsoft.AspNet.Mvc.Razor
public interface IRazorViewEngine : IViewEngine public interface IRazorViewEngine : IViewEngine
{ {
/// <summary> /// <summary>
/// Finds a <see cref="IRazorPage"/> using the same view discovery semantics used in /// Finds the page with the given <paramref name="pageName"/> using view locations and information from the
/// <see cref="IViewEngine.FindPartialView(ActionContext, string)"/>. /// <paramref name="context"/>.
/// </summary> /// </summary>
/// <param name="context">The <see cref="ActionContext"/>.</param> /// <param name="context">The <see cref="ActionContext"/>.</param>
/// <param name="page">The name or full path to the view.</param> /// <param name="pageName">The name of the page.</param>
/// <returns>A result representing the result of locating the <see cref="IRazorPage"/>.</returns> /// <param name="isPartial">Determines if the page being found is a partial.</param>
RazorPageResult FindPage(ActionContext context, string page); /// <returns>The <see cref="RazorPageResult"/> of locating the page.</returns>
/// <remarks>Page search semantics match <see cref="IViewEngine.FindView"/>.</remarks>
RazorPageResult FindPage(ActionContext context, string pageName, bool isPartial);
/// <summary>
/// Gets the page with the given <paramref name="pagePath"/>, relative to <paramref name="executingFilePath"/>
/// unless <paramref name="pagePath"/> is already absolute.
/// </summary>
/// <param name="executingFilePath">The absolute path to the currently-executing page, if any.</param>
/// <param name="pagePath">The path to the page.</param>
/// <param name="isPartial">Determines if the page being found is a partial.</param>
/// <returns>The <see cref="RazorPageResult"/> of locating the page.</returns>
/// <remarks>See also <see cref="IViewEngine.GetView"/>.</remarks>
RazorPageResult GetPage(string executingFilePath, string pagePath, bool isPartial);
/// <summary>
/// Converts the given <paramref name="pagePath"/> to be absolute, relative to
/// <paramref name="executingFilePath"/> unless <paramref name="pagePath"/> is already absolute.
/// </summary>
/// <param name="executingFilePath">The absolute path to the currently-executing page, if any.</param>
/// <param name="pagePath">The path to the page.</param>
/// <returns>
/// The combination of <paramref name="executingFilePath"/> and <paramref name="pagePath"/> if
/// <paramref name="pagePath"/> is a relative path. The <paramref name="pagePath"/> value (unchanged)
/// otherwise.
/// </returns>
string MakePathAbsolute(string executingFilePath, string pagePath);
} }
} }

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNet.Mvc.Razor
/// <summary> /// <summary>
/// Initializes a new instance of <see cref="RazorPageResult"/> for a successful discovery. /// Initializes a new instance of <see cref="RazorPageResult"/> for a successful discovery.
/// </summary> /// </summary>
/// <param name="name">The name of the page that was located.</param> /// <param name="name">The name of the page that was found.</param>
/// <param name="page">The located <see cref="IRazorPage"/>.</param> /// <param name="page">The located <see cref="IRazorPage"/>.</param>
public RazorPageResult(string name, IRazorPage page) public RazorPageResult(string name, IRazorPage page)
{ {
@ -36,7 +36,7 @@ namespace Microsoft.AspNet.Mvc.Razor
/// <summary> /// <summary>
/// Initializes a new instance of <see cref="RazorPageResult"/> for an unsuccessful discovery. /// Initializes a new instance of <see cref="RazorPageResult"/> for an unsuccessful discovery.
/// </summary> /// </summary>
/// <param name="name">The name of the page that was located.</param> /// <param name="name">The name of the page that was not found.</param>
/// <param name="searchedLocations">The locations that were searched.</param> /// <param name="searchedLocations">The locations that were searched.</param>
public RazorPageResult(string name, IEnumerable<string> searchedLocations) public RazorPageResult(string name, IEnumerable<string> searchedLocations)
{ {
@ -56,10 +56,8 @@ namespace Microsoft.AspNet.Mvc.Razor
} }
/// <summary> /// <summary>
/// Gets the name of the page being located. /// Gets the name or the path of the page being located.
/// </summary> /// </summary>
/// <remarks>This property maps to the <c>name</c> parameter of
/// <see cref="IRazorViewEngine.FindPage(ActionContext, string)"/>.</remarks>
public string Name { get; } public string Name { get; }
/// <summary> /// <summary>
@ -69,7 +67,7 @@ namespace Microsoft.AspNet.Mvc.Razor
public IRazorPage Page { get; } public IRazorPage Page { get; }
/// <summary> /// <summary>
/// Gets the locations that were searched when <see cref="Page"/> could not be located. /// Gets the locations that were searched when <see cref="Page"/> could not be found.
/// </summary> /// </summary>
/// <remarks>This property is <c>null</c> if the page was found.</remarks> /// <remarks>This property is <c>null</c> if the page was found.</remarks>
public IEnumerable<string> SearchedLocations { get; } public IEnumerable<string> SearchedLocations { get; }

View File

@ -166,10 +166,15 @@ namespace Microsoft.AspNet.Mvc.Razor
{ {
var viewStart = ViewStartPages[i]; var viewStart = ViewStartPages[i];
context.ExecutingFilePath = viewStart.Path; context.ExecutingFilePath = viewStart.Path;
// Copy the layout value from the previous view start (if any) to the current. // Copy the layout value from the previous view start (if any) to the current.
viewStart.Layout = layout; viewStart.Layout = layout;
await RenderPageCoreAsync(viewStart, context); 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 finally
@ -177,13 +182,13 @@ namespace Microsoft.AspNet.Mvc.Razor
context.ExecutingFilePath = oldFilePath; 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; RazorPage.Layout = layout;
} }
private async Task RenderLayoutAsync( private async Task RenderLayoutAsync(
ViewContext context, ViewContext context,
IBufferedTextWriter bodyWriter) IBufferedTextWriter bodyWriter)
{ {
// A layout page can specify another layout page. We'll need to continue // A layout page can specify another layout page. We'll need to continue
// looking for layout pages until they're no longer specified. // looking for layout pages until they're no longer specified.
@ -202,7 +207,7 @@ namespace Microsoft.AspNet.Mvc.Razor
throw new InvalidOperationException(message); throw new InvalidOperationException(message);
} }
var layoutPage = GetLayoutPage(context, previousPage.Layout); var layoutPage = GetLayoutPage(context, previousPage.Path, previousPage.Layout);
if (renderedLayouts.Count > 0 && if (renderedLayouts.Count > 0 &&
renderedLayouts.Any(l => string.Equals(l.Path, layoutPage.Path, StringComparison.Ordinal))) 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) if (layoutPageResult.Page == null)
{ {
var locations = Environment.NewLine + layoutPageResult = _viewEngine.FindPage(context, layoutPath, isPartial: true);
string.Join(Environment.NewLine, layoutPageResult.SearchedLocations); }
if (layoutPageResult.Page == null)
{
var locations =
Environment.NewLine + string.Join(Environment.NewLine, layoutPageResult.SearchedLocations);
throw new InvalidOperationException(Resources.FormatLayoutCannotBeLocated(layoutPath, locations)); throw new InvalidOperationException(Resources.FormatLayoutCannotBeLocated(layoutPath, locations));
} }

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.Linq;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using Microsoft.AspNet.Mvc.Routing; using Microsoft.AspNet.Mvc.Routing;
using Microsoft.AspNet.Mvc.ViewEngines; using Microsoft.AspNet.Mvc.ViewEngines;
@ -63,7 +64,7 @@ namespace Microsoft.AspNet.Mvc.Razor
/// which contains following indexes: /// which contains following indexes:
/// {0} - Action Name /// {0} - Action Name
/// {1} - Controller 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 <c>Test</c> action of <c>HomeController</c> should be located at /// For example, the view for the <c>Test</c> action of <c>HomeController</c> should be located at
/// <c>/Views/Home/Test.cshtml</c>. Locations such as <c>/views/home/test.cshtml</c> would not be discovered /// <c>/Views/Home/Test.cshtml</c>. Locations such as <c>/views/home/test.cshtml</c> would not be discovered
/// </remarks> /// </remarks>
@ -84,7 +85,7 @@ namespace Microsoft.AspNet.Mvc.Razor
/// {0} - Action Name /// {0} - Action Name
/// {1} - Controller Name /// {1} - Controller Name
/// {2} - Area 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 <c>Test</c> action of <c>HomeController</c> should be located at /// For example, the view for the <c>Test</c> action of <c>HomeController</c> should be located at
/// <c>/Views/Home/Test.cshtml</c>. Locations such as <c>/views/home/test.cshtml</c> would not be discovered /// <c>/Views/Home/Test.cshtml</c>. Locations such as <c>/views/home/test.cshtml</c> would not be discovered
/// </remarks> /// </remarks>
@ -100,65 +101,6 @@ namespace Microsoft.AspNet.Mvc.Razor
/// </summary> /// </summary>
protected IMemoryCache ViewLookupCache { get; } protected IMemoryCache ViewLookupCache { get; }
/// <inheritdoc />
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);
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
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);
}
}
/// <summary> /// <summary>
/// Gets the case-normalized route value for the specified route <paramref name="key"/>. /// Gets the case-normalized route value for the specified route <paramref name="key"/>.
/// </summary> /// </summary>
@ -232,29 +174,109 @@ namespace Microsoft.AspNet.Mvc.Razor
return stringRouteValue; return stringRouteValue;
} }
private ViewLocationCacheResult GetViewLocationCacheResult( /// <inheritdoc />
ActionContext context, public RazorPageResult FindPage(ActionContext context, string pageName, bool isPartial)
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<string>());
}
var cacheResult = LocatePageFromViewLocations(context, pageName, isPartial);
if (cacheResult.Success)
{
var razorPage = cacheResult.ViewEntry.PageFactory();
razorPage.IsPartial = isPartial;
return new RazorPageResult(pageName, razorPage);
} }
else else
{ {
return LocatePageFromViewLocations(context, pageName, isPartial); return new RazorPageResult(pageName, cacheResult.SearchedLocations);
} }
} }
private ViewLocationCacheResult LocatePageFromPath(string pageName, bool isPartial) /// <inheritdoc />
public RazorPageResult GetPage(string executingFilePath, string pagePath, bool isPartial)
{ {
var applicationRelativePath = pageName; if (string.IsNullOrEmpty(pagePath))
if (!pageName.EndsWith(ViewExtension, StringComparison.OrdinalIgnoreCase))
{ {
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<string>());
}
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);
}
}
/// <inheritdoc />
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<string>());
}
var cacheResult = LocatePageFromViewLocations(context, viewName, isPartial);
return CreateViewEngineResult(cacheResult, viewName, isPartial);
}
/// <inheritdoc />
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<string>());
}
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); var cacheKey = new ViewLocationCacheKey(applicationRelativePath, isPartial);
ViewLocationCacheResult cacheResult; ViewLocationCacheResult cacheResult;
if (!ViewLookupCache.TryGetValue(cacheKey, out 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. // No views were found at the specified location. Create a not found result.
if (cacheResult == null) if (cacheResult == null)
{ {
cacheResult = new ViewLocationCacheResult(new[] { pageName }); cacheResult = new ViewLocationCacheResult(new[] { applicationRelativePath });
} }
cacheResult = ViewLookupCache.Set<ViewLocationCacheResult>( cacheResult = ViewLookupCache.Set<ViewLocationCacheResult>(
@ -327,6 +349,42 @@ namespace Microsoft.AspNet.Mvc.Razor
return cacheResult; return cacheResult;
} }
/// <inheritdoc />
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( private ViewLocationCacheResult OnCacheMiss(
ViewLocationExpanderContext expanderContext, ViewLocationExpanderContext expanderContext,
ViewLocationCacheKey cacheKey) ViewLocationCacheKey cacheKey)
@ -451,7 +509,7 @@ namespace Microsoft.AspNet.Mvc.Razor
for (var i = 0; i < viewStarts.Length; i++) for (var i = 0; i < viewStarts.Length; i++)
{ {
var viewStartItem = result.ViewStartEntries[i]; var viewStartItem = result.ViewStartEntries[i];
viewStarts[i] = result.ViewStartEntries[i].PageFactory(); viewStarts[i] = viewStartItem.PageFactory();
} }
var view = new RazorView( var view = new RazorView(
@ -469,5 +527,13 @@ namespace Microsoft.AspNet.Mvc.Razor
Debug.Assert(!string.IsNullOrEmpty(name)); Debug.Assert(!string.IsNullOrEmpty(name));
return name[0] == '~' || name[0] == '/'; 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);
}
} }
} }

View File

@ -130,6 +130,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
ValidationSummaryMessageElement = viewContext.ValidationSummaryMessageElement; ValidationSummaryMessageElement = viewContext.ValidationSummaryMessageElement;
ValidationMessageElement = viewContext.ValidationMessageElement; ValidationMessageElement = viewContext.ValidationMessageElement;
ExecutingFilePath = viewContext.ExecutingFilePath;
View = view; View = view;
ViewData = viewData; ViewData = viewData;
TempData = viewContext.TempData; TempData = viewContext.TempData;

View File

@ -76,18 +76,19 @@ namespace Microsoft.AspNet.Mvc.ViewComponents
throw new ArgumentNullException(nameof(context)); throw new ArgumentNullException(nameof(context));
} }
var viewEngine = ViewEngine ?? ResolveViewEngine(context); var viewContext = context.ViewContext;
var viewData = ViewData ?? context.ViewData; var viewData = ViewData ?? context.ViewData;
var viewEngine = ViewEngine ?? ResolveViewEngine(context);
var isNullOrEmptyViewName = string.IsNullOrEmpty(ViewName); var isNullOrEmptyViewName = string.IsNullOrEmpty(ViewName);
string qualifiedViewName; ViewEngineResult result = null;
if (!isNullOrEmptyViewName && if (!isNullOrEmptyViewName)
(ViewName[0] == '~' || ViewName[0] == '/'))
{ {
// View name that was passed in is already a rooted path, the view engine will handle this. // If view name was passed in is already a path, the view engine will handle this.
qualifiedViewName = ViewName; result = viewEngine.GetView(viewContext.ExecutingFilePath, ViewName, isPartial: true);
} }
else
if (result == null || !result.Success)
{ {
// This will produce a string like: // 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. // This supports a controller or area providing an override for component views.
var viewName = isNullOrEmptyViewName ? DefaultViewName : ViewName; var viewName = isNullOrEmptyViewName ? DefaultViewName : ViewName;
var qualifiedViewName = string.Format(
qualifiedViewName = string.Format(
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
ViewPathFormat, ViewPathFormat,
context.ViewComponentDescriptor.ShortName, context.ViewComponentDescriptor.ShortName,
viewName); viewName);
result = viewEngine.FindView(viewContext, qualifiedViewName, isPartial: true);
} }
var view = FindView(context.ViewContext, viewEngine, qualifiedViewName); var view = result.EnsureSuccessful().View;
var childViewContext = new ViewContext(
context.ViewContext,
view,
ViewData ?? context.ViewData,
context.Writer);
using (view as IDisposable) using (view as IDisposable)
{ {
if (_diagnosticSource == null) if (_diagnosticSource == null)
{ {
_diagnosticSource = context.ViewContext.HttpContext.RequestServices.GetRequiredService<DiagnosticSource>(); _diagnosticSource = viewContext.HttpContext.RequestServices.GetRequiredService<DiagnosticSource>();
} }
_diagnosticSource.ViewComponentBeforeViewExecute(context, view); _diagnosticSource.ViewComponentBeforeViewExecute(context, view);
var childViewContext = new ViewContext(viewContext, view, ViewData ?? context.ViewData, context.Writer);
await view.RenderAsync(childViewContext); await view.RenderAsync(childViewContext);
_diagnosticSource.ViewComponentAfterViewExecute(context, view); _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) private static IViewEngine ResolveViewEngine(ViewComponentContext context)
{ {
return context.ViewContext.HttpContext.RequestServices.GetRequiredService<ICompositeViewEngine>(); return context.ViewContext.HttpContext.RequestServices.GetRequiredService<ICompositeViewEngine>();

View File

@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.Extensions.OptionsModel; using Microsoft.Extensions.OptionsModel;
@ -11,6 +10,8 @@ namespace Microsoft.AspNet.Mvc.ViewEngines
/// <inheritdoc /> /// <inheritdoc />
public class CompositeViewEngine : ICompositeViewEngine public class CompositeViewEngine : ICompositeViewEngine
{ {
private const string ViewExtension = ".cshtml";
/// <summary> /// <summary>
/// Initializes a new instance of <see cref="CompositeViewEngine"/>. /// Initializes a new instance of <see cref="CompositeViewEngine"/>.
/// </summary> /// </summary>
@ -24,61 +25,53 @@ namespace Microsoft.AspNet.Mvc.ViewEngines
public IReadOnlyList<IViewEngine> ViewEngines { get; } public IReadOnlyList<IViewEngine> ViewEngines { get; }
/// <inheritdoc /> /// <inheritdoc />
public ViewEngineResult FindPartialView( public ViewEngineResult FindView(ActionContext context, string viewName, bool isPartial)
ActionContext context,
string partialViewName)
{ {
if (context == null) List<string> searchedLocations = null;
{
throw new ArgumentNullException(nameof(context));
}
if (partialViewName == null)
{
throw new ArgumentNullException(nameof(partialViewName));
}
return FindView(context, partialViewName, partial: true);
}
/// <inheritdoc />
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<string>();
foreach (var engine in ViewEngines) foreach (var engine in ViewEngines)
{ {
var result = partial ? engine.FindPartialView(context, viewName) : var result = engine.FindView(context, viewName, isPartial);
engine.FindView(context, viewName);
if (result.Success) if (result.Success)
{ {
return result; return result;
} }
searchedLocations = searchedLocations.Concat(result.SearchedLocations); if (searchedLocations == null)
{
searchedLocations = new List<string>(result.SearchedLocations);
}
else
{
searchedLocations.AddRange(result.SearchedLocations);
}
} }
return ViewEngineResult.NotFound(viewName, searchedLocations); return ViewEngineResult.NotFound(viewName, searchedLocations ?? Enumerable.Empty<string>());
}
/// <inheritdoc />
public ViewEngineResult GetView(string executingFilePath, string viewPath, bool isPartial)
{
List<string> 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<string>(result.SearchedLocations);
}
else
{
searchedLocations.AddRange(result.SearchedLocations);
}
}
return ViewEngineResult.NotFound(viewPath, searchedLocations ?? Enumerable.Empty<string>());
} }
} }
} }

View File

@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Mvc.ViewEngines namespace Microsoft.AspNet.Mvc.ViewEngines
{ {
/// <summary> /// <summary>
@ -10,19 +9,23 @@ namespace Microsoft.AspNet.Mvc.ViewEngines
public interface IViewEngine public interface IViewEngine
{ {
/// <summary> /// <summary>
/// Finds the specified view by using the specified action context. /// Finds the view with the given <paramref name="viewName"/> using view locations and information from the
/// <paramref name="context"/>.
/// </summary> /// </summary>
/// <param name="context">The action context.</param> /// <param name="context">The <see cref="ActionContext"/>.</param>
/// <param name="viewName">The name or full path to the view.</param> /// <param name="viewName">The name of the view.</param>
/// <returns>A result representing the result of locating the view.</returns> /// <param name="isPartial">Determines if the view being found is a partial.</param>
ViewEngineResult FindView(ActionContext context, string viewName); /// <returns>The <see cref="ViewEngineResult"/> of locating the view.</returns>
ViewEngineResult FindView(ActionContext context, string viewName, bool isPartial);
/// <summary> /// <summary>
/// Finds the specified partial view by using the specified action context. /// Gets the view with the given <paramref name="viewPath"/>, relative to <paramref name="executingFilePath"/>
/// unless <paramref name="viewPath"/> is already absolute.
/// </summary> /// </summary>
/// <param name="context">The action context.</param> /// <param name="executingFilePath">The absolute path to the currently-executing view, if any.</param>
/// <param name="partialViewName">The name or full path to the view.</param> /// <param name="viewPath">The path to the view.</param>
/// <returns>A result representing the result of locating the view.</returns> /// <param name="isPartial">Determines if the view being found is a partial.</param>
ViewEngineResult FindPartialView(ActionContext context, string partialViewName); /// <returns>The <see cref="ViewEngineResult"/> of locating the view.</returns>
ViewEngineResult GetView(string executingFilePath, string viewPath, bool isPartial);
} }
} }

View File

@ -510,39 +510,45 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
return RenderPartialCoreAsync(partialViewName, model, viewData, ViewContext.Writer); return RenderPartialCoreAsync(partialViewName, model, viewData, ViewContext.Writer);
} }
protected virtual IHtmlContent GenerateDisplay(ModelExplorer modelExplorer, protected virtual IHtmlContent GenerateDisplay(
string htmlFieldName, ModelExplorer modelExplorer,
string templateName, string htmlFieldName,
object additionalViewData) string templateName,
object additionalViewData)
{ {
var templateBuilder = new TemplateBuilder(_viewEngine, var templateBuilder = new TemplateBuilder(
ViewContext, _viewEngine,
ViewData, ViewContext,
modelExplorer, ViewData,
htmlFieldName, modelExplorer,
templateName, htmlFieldName,
readOnly: true, templateName,
additionalViewData: additionalViewData); readOnly: true,
additionalViewData: additionalViewData);
return templateBuilder.Build(); return templateBuilder.Build();
} }
protected virtual async Task RenderPartialCoreAsync(string partialViewName, protected virtual async Task RenderPartialCoreAsync(
object model, string partialViewName,
ViewDataDictionary viewData, object model,
TextWriter writer) ViewDataDictionary viewData,
TextWriter writer)
{ {
if (partialViewName == null) if (partialViewName == null)
{ {
throw new ArgumentNullException(nameof(partialViewName)); throw new ArgumentNullException(nameof(partialViewName));
} }
// Determine which ViewData we should use to construct a new ViewData var viewEngineResult = _viewEngine.GetView(
var baseViewData = viewData ?? ViewData; 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) if (!viewEngineResult.Success)
{ {
var locations = string.Empty; var locations = string.Empty;
@ -559,7 +565,12 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
var view = viewEngineResult.View; var view = viewEngineResult.View;
using (view as IDisposable) 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); var viewContext = new ViewContext(ViewContext, view, newViewData, writer);
await viewEngineResult.View.RenderAsync(viewContext); await viewEngineResult.View.RenderAsync(viewContext);
} }
} }

View File

@ -69,7 +69,12 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
var viewEngine = viewResult.ViewEngine ?? ViewEngine; var viewEngine = viewResult.ViewEngine ?? ViewEngine;
var viewName = viewResult.ViewName ?? actionContext.ActionDescriptor.Name; 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) if (result.Success)
{ {
DiagnosticSource.ViewFound(actionContext, true, viewResult, viewName, result.View); DiagnosticSource.ViewFound(actionContext, true, viewResult, viewName, result.View);

View File

@ -109,9 +109,14 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures.Internal
foreach (string viewName in GetViewNames()) 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) if (viewEngineResult.Success)
{ {
using (var writer = new StringCollectionTextWriter(_viewContext.Writer.Encoding)) using (var writer = new StringCollectionTextWriter(_viewContext.Writer.Encoding))

View File

@ -68,7 +68,12 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
var viewEngine = viewResult.ViewEngine ?? ViewEngine; var viewEngine = viewResult.ViewEngine ?? ViewEngine;
var viewName = viewResult.ViewName ?? actionContext.ActionDescriptor.Name; 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 (result.Success)
{ {
if (DiagnosticSource.IsEnabled("Microsoft.AspNet.Mvc.ViewFound")) if (DiagnosticSource.IsEnabled("Microsoft.AspNet.Mvc.ViewFound"))

View File

@ -20,9 +20,9 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
[Theory] [Theory]
[InlineData("LayoutSpecifiedWithPartialPathInViewStart")] [InlineData("LayoutSpecifiedWithPartialPathInViewStart")]
[InlineData("LayoutSpecifiedWithPartialPathInViewStart_ForViewSpecifiedWithAppRelativePath")] [InlineData("LayoutSpecifiedWithPartialPathInViewStart_ForViewSpecifiedWithRelativePath")]
[InlineData("LayoutSpecifiedWithPartialPathInViewStart_ForViewSpecifiedWithPartialName")] [InlineData("LayoutSpecifiedWithPartialPathInViewStart_ForViewSpecifiedWithPartialName")]
[InlineData("LayoutSpecifiedWithPartialPathInViewStart_ForViewSpecifiedWithAppRelativePathWithExtension")] [InlineData("LayoutSpecifiedWithPartialPathInViewStart_ForViewSpecifiedWithAppRelativePath")]
public async Task PartialLayoutPaths_SpecifiedInViewStarts_GetResolvedByViewEngine(string action) public async Task PartialLayoutPaths_SpecifiedInViewStarts_GetResolvedByViewEngine(string action)
{ {
// Arrange // Arrange
@ -41,8 +41,8 @@ _ViewStart that specifies partial Layout
[Theory] [Theory]
[InlineData("LayoutSpecifiedWithPartialPathInPage")] [InlineData("LayoutSpecifiedWithPartialPathInPage")]
[InlineData("LayoutSpecifiedWithPartialPathInPageWithPartialPath")] [InlineData("LayoutSpecifiedWithPartialPathInPageWithPartialPath")]
[InlineData("LayoutSpecifiedWithPartialPathInPageWithRelativePath")]
[InlineData("LayoutSpecifiedWithPartialPathInPageWithAppRelativePath")] [InlineData("LayoutSpecifiedWithPartialPathInPageWithAppRelativePath")]
[InlineData("LayoutSpecifiedWithPartialPathInPageWithAppRelativePathWithExtension")]
public async Task PartialLayoutPaths_SpecifiedInPage_GetResolvedByViewEngine(string actionName) public async Task PartialLayoutPaths_SpecifiedInPage_GetResolvedByViewEngine(string actionName)
{ {
// Arrange // Arrange
@ -58,8 +58,8 @@ _ViewStart that specifies partial Layout
} }
[Theory] [Theory]
[InlineData("LayoutSpecifiedWithNonPartialPath")] [InlineData("LayoutSpecifiedWithRelativePath")]
[InlineData("LayoutSpecifiedWithNonPartialPathWithExtension")] [InlineData("LayoutSpecifiedWithAppRelativePath")]
public async Task NonPartialLayoutPaths_GetResolvedByViewEngine(string actionName) public async Task NonPartialLayoutPaths_GetResolvedByViewEngine(string actionName)
{ {
// Arrange // Arrange
@ -76,8 +76,8 @@ _ViewStart that specifies partial Layout
[Theory] [Theory]
[InlineData("ViewWithPartial_SpecifiedWithPartialName")] [InlineData("ViewWithPartial_SpecifiedWithPartialName")]
[InlineData("ViewWithPartial_SpecifiedWithAbsoluteName")] [InlineData("ViewWithPartial_SpecifiedWithRelativePath")]
[InlineData("ViewWithPartial_SpecifiedWithAbsoluteNameAndExtension")] [InlineData("ViewWithPartial_SpecifiedWithAppRelativePath")]
public async Task PartialsCanBeSpecifiedWithPartialPath(string actionName) public async Task PartialsCanBeSpecifiedWithPartialPath(string actionName)
{ {
// Arrange // Arrange

View File

@ -326,6 +326,30 @@ Page Content
Assert.Equal(expected, body.Trim(), ignoreLineEndingDifferences: true); Assert.Equal(expected, body.Trim(), ignoreLineEndingDifferences: true);
} }
[Fact]
public async Task RelativePathsWorkAsExpected()
{
// Arrange
var expected =
@"<layout>
<nested-layout>
/ViewEngine/ViewWithRelativePath
ViewWithRelativePath-content
<partial>partial-content</partial>
<component-title>View with relative path title</component-title>
<component-body>Component with Relative Path
<label><strong>Name:</strong> Fred</label>
WriteLiteral says:<strong>Write says:98052WriteLiteral says:</strong></component-body>
</nested-layout>
</layout>";
// Act
var body = await Client.GetStringAsync("http://localhost/ViewEngine/ViewWithRelativePath");
// Assert
Assert.Equal(expected, body.Trim(), ignoreLineEndingDifferences: true);
}
[Fact] [Fact]
public async Task ViewComponentsDoNotExecuteViewStarts() public async Task ViewComponentsDoNotExecuteViewStarts()
{ {

View File

@ -32,7 +32,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
{"controller", "bar"}, {"controller", "bar"},
}; };
public static IEnumerable<string[]> InvalidViewNameValues public static IEnumerable<string[]> AbsoluteViewPathData
{ {
get get
{ {
@ -80,43 +80,75 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var context = GetActionContext(_controllerTestContext); var context = GetActionContext(_controllerTestContext);
// Act & Assert // Act & Assert
ExceptionAssert.ThrowsArgumentNullOrEmpty(() => viewEngine.FindView(context, viewName), "viewName"); ExceptionAssert.ThrowsArgumentNullOrEmpty(
() => viewEngine.FindView(context, viewName, isPartial: false),
"viewName");
} }
[Theory] [Theory]
[MemberData(nameof(InvalidViewNameValues))] [MemberData(nameof(AbsoluteViewPathData))]
public void FindView_WithFullPathReturnsNotFound_WhenPathDoesNotMatchExtension(string viewName) public void FindView_WithFullPath_ReturnsNotFound(string viewName)
{ {
// Arrange // Arrange
var viewEngine = CreateViewEngine(); var viewEngine = CreateSuccessfulViewEngine();
var context = GetActionContext(_controllerTestContext); var context = GetActionContext(_controllerTestContext);
// Act // Act
var result = viewEngine.FindView(context, viewName); var result = viewEngine.FindView(context, viewName, isPartial: false);
// Assert // Assert
Assert.False(result.Success); Assert.False(result.Success);
} }
[Theory] [Theory]
[MemberData(nameof(InvalidViewNameValues))] [MemberData(nameof(AbsoluteViewPathData))]
public void FindViewFullPathSucceedsWithCshtmlEnding(string viewName) public void FindView_WithFullPathAndCshtmlEnding_ReturnsNotFound(string viewName)
{ {
// Arrange // Arrange
var viewEngine = CreateViewEngine(); var viewEngine = CreateSuccessfulViewEngine();
// Append .cshtml so the viewname is no longer invalid var context = GetActionContext(_controllerTestContext);
viewName += ".cshtml"; 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); var context = GetActionContext(_controllerTestContext);
// Act & Assert // Act
// If this throws then our test case fails var result = viewEngine.FindView(context, "View.cshtml", isPartial);
var result = viewEngine.FindPartialView(context, viewName);
// 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); Assert.False(result.Success);
} }
[Fact] [Fact]
public void FindPartialView_ReturnsRazorView_IfLookupWasSuccessful() public void FindView_IsPartial_ReturnsRazorView_IfLookupWasSuccessful()
{ {
// Arrange // Arrange
var pageFactory = new Mock<IRazorPageFactoryProvider>(); var pageFactory = new Mock<IRazorPageFactoryProvider>();
@ -140,7 +172,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var context = GetActionContext(_controllerTestContext); var context = GetActionContext(_controllerTestContext);
// Act // Act
var result = viewEngine.FindPartialView(context, "test-view"); var result = viewEngine.FindView(context, "test-view", isPartial: true);
// Assert // Assert
Assert.True(result.Success); Assert.True(result.Success);
@ -151,7 +183,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
} }
[Fact] [Fact]
public void FindPartialView_DoesNotExpireCachedResults_IfViewStartsExpire() public void FindView_IsPartial_DoesNotExpireCachedResults_IfViewStartsExpire()
{ {
// Arrange // Arrange
var pageFactory = new Mock<IRazorPageFactoryProvider>(); var pageFactory = new Mock<IRazorPageFactoryProvider>();
@ -172,7 +204,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var context = GetActionContext(_controllerTestContext); var context = GetActionContext(_controllerTestContext);
// Act - 1 // Act - 1
var result1 = viewEngine.FindPartialView(context, "test-view"); var result1 = viewEngine.FindView(context, "test-view", isPartial: true);
// Assert - 1 // Assert - 1
Assert.True(result1.Success); Assert.True(result1.Success);
@ -183,7 +215,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
// Act - 2 // Act - 2
cancellationTokenSource.Cancel(); cancellationTokenSource.Cancel();
var result2 = viewEngine.FindPartialView(context, "test-view"); var result2 = viewEngine.FindView(context, "test-view", isPartial: true);
// Assert - 2 // Assert - 2
Assert.True(result2.Success); Assert.True(result2.Success);
@ -195,7 +227,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
[Theory] [Theory]
[InlineData(null)] [InlineData(null)]
[InlineData("")] [InlineData("")]
public void FindPartialView_ThrowsIfViewNameIsNullOrEmpty(string partialViewName) public void FindView_IsPartial_ThrowsIfViewNameIsNullOrEmpty(string partialViewName)
{ {
// Arrange // Arrange
var viewEngine = CreateViewEngine(); var viewEngine = CreateViewEngine();
@ -203,52 +235,50 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
// Act & Assert // Act & Assert
ExceptionAssert.ThrowsArgumentNullOrEmpty( ExceptionAssert.ThrowsArgumentNullOrEmpty(
() => viewEngine.FindPartialView(context, partialViewName), () => viewEngine.FindView(context, partialViewName, isPartial: true),
"partialViewName"); "viewName");
} }
[Theory] [Theory]
[MemberData(nameof(InvalidViewNameValues))] [MemberData(nameof(AbsoluteViewPathData))]
public void FindPartialView_WithFullPathReturnsNotFound_WhenPathDoesNotMatchExtension(string partialViewName) public void FindView_IsPartialWithFullPath_ReturnsNotFound(string partialViewName)
{ {
// Arrange // Arrange
var viewEngine = CreateViewEngine(); var viewEngine = CreateSuccessfulViewEngine();
var context = GetActionContext(_controllerTestContext); var context = GetActionContext(_controllerTestContext);
// Act // Act
var result = viewEngine.FindPartialView(context, partialViewName); var result = viewEngine.FindView(context, partialViewName, isPartial: true);
// Assert // Assert
Assert.False(result.Success); Assert.False(result.Success);
} }
[Theory] [Theory]
[MemberData(nameof(InvalidViewNameValues))] [MemberData(nameof(AbsoluteViewPathData))]
public void FindPartialViewFullPathSucceedsWithCshtmlEnding(string partialViewName) public void FindView_IsPartialWithFullPathAndCshtmlEnding_ReturnsNotFound(string partialViewName)
{ {
// Arrange // Arrange
var viewEngine = CreateViewEngine(); var viewEngine = CreateSuccessfulViewEngine();
// Append .cshtml so the viewname is no longer invalid
partialViewName += ".cshtml";
var context = GetActionContext(_controllerTestContext); var context = GetActionContext(_controllerTestContext);
partialViewName += ".cshtml";
// Act & Assert // Act
// If this throws then our test case fails var result = viewEngine.FindView(context, partialViewName, isPartial: true);
var result = viewEngine.FindPartialView(context, partialViewName);
// Assert
Assert.False(result.Success); Assert.False(result.Success);
} }
[Fact] [Fact]
public void FindPartialViewFailureSearchesCorrectLocationsWithAreas() public void FindView_IsPartial_FailsButSearchesCorrectLocations_WithAreas()
{ {
// Arrange // Arrange
var searchedLocations = new List<string>();
var viewEngine = CreateViewEngine(); var viewEngine = CreateViewEngine();
var context = GetActionContext(_areaTestContext); var context = GetActionContext(_areaTestContext);
// Act // Act
var result = viewEngine.FindPartialView(context, "partial"); var result = viewEngine.FindView(context, "partial", isPartial: true);
// Assert // Assert
Assert.False(result.Success); Assert.False(result.Success);
@ -261,14 +291,14 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
} }
[Fact] [Fact]
public void FindPartialViewFailureSearchesCorrectLocationsWithoutAreas() public void FindView_IsPartial_FailsButSearchesCorrectLocations_WithoutAreas()
{ {
// Arrange // Arrange
var viewEngine = CreateViewEngine(); var viewEngine = CreateViewEngine();
var context = GetActionContext(_controllerTestContext); var context = GetActionContext(_controllerTestContext);
// Act // Act
var result = viewEngine.FindPartialView(context, "partialNoArea"); var result = viewEngine.FindView(context, "partialNoArea", isPartial: true);
// Assert // Assert
Assert.False(result.Success); Assert.False(result.Success);
@ -279,14 +309,14 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
} }
[Fact] [Fact]
public void FindViewFailureSearchesCorrectLocationsWithAreas() public void FindView_FailsButSearchesCorrectLocationsWithAreas()
{ {
// Arrange // Arrange
var viewEngine = CreateViewEngine(); var viewEngine = CreateViewEngine();
var context = GetActionContext(_areaTestContext); var context = GetActionContext(_areaTestContext);
// Act // Act
var result = viewEngine.FindView(context, "full"); var result = viewEngine.FindView(context, "full", isPartial: false);
// Assert // Assert
Assert.False(result.Success); Assert.False(result.Success);
@ -298,14 +328,14 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
} }
[Fact] [Fact]
public void FindViewFailureSearchesCorrectLocationsWithoutAreas() public void FindView_FailsButSearchesCorrectLocationsWithoutAreas()
{ {
// Arrange // Arrange
var viewEngine = CreateViewEngine(); var viewEngine = CreateViewEngine();
var context = GetActionContext(_controllerTestContext); var context = GetActionContext(_controllerTestContext);
// Act // Act
var result = viewEngine.FindView(context, "fullNoArea"); var result = viewEngine.FindView(context, "fullNoArea", isPartial: false);
// Assert // Assert
Assert.False(result.Success); Assert.False(result.Success);
@ -340,7 +370,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var context = GetActionContext(_controllerTestContext); var context = GetActionContext(_controllerTestContext);
// Act // Act
var result = viewEngine.FindView(context, "test-view"); var result = viewEngine.FindView(context, "test-view", isPartial: false);
// Assert // Assert
Assert.True(result.Success); Assert.True(result.Success);
@ -371,7 +401,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var context = GetActionContext(_controllerTestContext); var context = GetActionContext(_controllerTestContext);
// Act // Act
var result = viewEngine.FindView(context, "test-view"); var result = viewEngine.FindView(context, "test-view", isPartial: false);
// Assert // Assert
pageFactory.Verify(); pageFactory.Verify();
@ -383,10 +413,12 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
public void FindView_UsesAreaViewLocationFormat_IfRouteContainsArea() public void FindView_UsesAreaViewLocationFormat_IfRouteContainsArea()
{ {
// Arrange // Arrange
var viewName = "test-view2";
var expectedViewName = "fake-area-path/foo/bar/test-view2.rzr";
var pageFactory = new Mock<IRazorPageFactoryProvider>(); var pageFactory = new Mock<IRazorPageFactoryProvider>();
var page = Mock.Of<IRazorPage>(); var page = Mock.Of<IRazorPage>();
pageFactory 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])) .Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
.Verifiable(); .Verifiable();
var viewEngine = new TestableRazorViewEngine( var viewEngine = new TestableRazorViewEngine(
@ -398,9 +430,147 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var context = GetActionContext(_areaTestContext); var context = GetActionContext(_areaTestContext);
// Act // Act
var result = viewEngine.FindView(context, "test-view2"); var result = viewEngine.FindView(context, viewName, isPartial: false);
// Assert // 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<IRazorPageFactoryProvider>();
var page = Mock.Of<IRazorPage>();
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<IRazorPageFactoryProvider>();
var page = Mock.Of<IRazorPage>();
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<IRazorPageFactoryProvider>();
var page = Mock.Of<IRazorPage>();
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<IRazorPageFactoryProvider>();
var page = Mock.Of<IRazorPage>();
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<IRazorPageFactoryProvider>();
var page = Mock.Of<IRazorPage>();
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(); pageFactory.Verify();
var view = Assert.IsType<RazorView>(result.View); var view = Assert.IsType<RazorView>(result.View);
Assert.Same(page, view.RazorPage); Assert.Same(page, view.RazorPage);
@ -459,7 +629,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var context = GetActionContext(routeValues); var context = GetActionContext(routeValues);
// Act // Act
var result = viewEngine.FindView(context, "test-view"); var result = viewEngine.FindView(context, "test-view", isPartial: false);
// Assert // Assert
Assert.True(result.Success); Assert.True(result.Success);
@ -488,7 +658,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var context = GetActionContext(_controllerTestContext); var context = GetActionContext(_controllerTestContext);
// Act 1 // Act 1
var result1 = viewEngine.FindView(context, "baz"); var result1 = viewEngine.FindView(context, "baz", isPartial: false);
// Assert 1 // Assert 1
Assert.True(result1.Success); Assert.True(result1.Success);
@ -501,7 +671,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
.Setup(p => p.CreateFactory(It.IsAny<string>())) .Setup(p => p.CreateFactory(It.IsAny<string>()))
.Throws(new Exception("Shouldn't be called")); .Throws(new Exception("Shouldn't be called"));
var result2 = viewEngine.FindView(context, "baz"); var result2 = viewEngine.FindView(context, "baz", isPartial: false);
// Assert 2 // Assert 2
Assert.True(result2.Success); Assert.True(result2.Success);
@ -539,7 +709,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var context = GetActionContext(_controllerTestContext); var context = GetActionContext(_controllerTestContext);
// Act 1 // Act 1
var result1 = viewEngine.FindView(context, "baz"); var result1 = viewEngine.FindView(context, "baz", isPartial: false);
// Assert 1 // Assert 1
Assert.True(result1.Success); Assert.True(result1.Success);
@ -548,7 +718,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
// Act 2 // Act 2
cancellationTokenSource.Cancel(); cancellationTokenSource.Cancel();
var result2 = viewEngine.FindView(context, "baz"); var result2 = viewEngine.FindView(context, "baz", isPartial: false);
// Assert 2 // Assert 2
Assert.True(result2.Success); Assert.True(result2.Success);
@ -591,7 +761,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var context = GetActionContext(_controllerTestContext); var context = GetActionContext(_controllerTestContext);
// Act 1 // Act 1
var result1 = viewEngine.FindView(context, "baz"); var result1 = viewEngine.FindView(context, "baz", isPartial: false);
// Assert 1 // Assert 1
Assert.True(result1.Success); Assert.True(result1.Success);
@ -601,7 +771,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
// Act 2 // Act 2
cancellationTokenSource.Cancel(); cancellationTokenSource.Cancel();
var result2 = viewEngine.FindView(context, "baz"); var result2 = viewEngine.FindView(context, "baz", isPartial: false);
// Assert 2 // Assert 2
Assert.True(result2.Success); Assert.True(result2.Success);
@ -646,7 +816,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var context = GetActionContext(_controllerTestContext); var context = GetActionContext(_controllerTestContext);
// Act - 1 // Act - 1
var result = viewEngine.FindView(context, "myview"); var result = viewEngine.FindView(context, "myview", isPartial: false);
// Assert - 1 // Assert - 1
Assert.False(result.Success); Assert.False(result.Success);
@ -654,7 +824,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
expander.Verify(); expander.Verify();
// Act - 2 // Act - 2
result = viewEngine.FindView(context, "myview"); result = viewEngine.FindView(context, "myview", isPartial: false);
// Assert - 2 // Assert - 2
Assert.False(result.Success); Assert.False(result.Success);
@ -705,7 +875,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var context = GetActionContext(_controllerTestContext); var context = GetActionContext(_controllerTestContext);
// Act - 1 // Act - 1
var result = viewEngine.FindView(context, "MyView"); var result = viewEngine.FindView(context, "MyView", isPartial: false);
// Assert - 1 // Assert - 1
Assert.False(result.Success); Assert.False(result.Success);
@ -717,7 +887,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
.Setup(p => p.CreateFactory("viewlocation3")) .Setup(p => p.CreateFactory("viewlocation3"))
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0])); .Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]));
cancellationTokenSource.Cancel(); cancellationTokenSource.Cancel();
result = viewEngine.FindView(context, "MyView"); result = viewEngine.FindView(context, "MyView", isPartial: false);
// Assert - 2 // Assert - 2
Assert.True(result.Success); Assert.True(result.Success);
@ -731,23 +901,86 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
Times.Exactly(2)); 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] [Theory]
[InlineData(null)] [InlineData(null)]
[InlineData("")] [InlineData("")]
public void FindPage_ThrowsIfNameIsNullOrEmpty(string pageName) public void FindPage_IsPartial_ThrowsIfNameIsNullOrEmpty(string pageName)
{ {
// Arrange // Arrange
var viewEngine = CreateViewEngine(); var viewEngine = CreateViewEngine();
var context = GetActionContext(_controllerTestContext); var context = GetActionContext(_controllerTestContext);
// Act & Assert // Act & Assert
ExceptionAssert.ThrowsArgumentNullOrEmpty(() => viewEngine.FindPage(context, pageName), ExceptionAssert.ThrowsArgumentNullOrEmpty(
"pageName"); () => viewEngine.FindPage(context, pageName, isPartial: true),
"pageName");
} }
[Theory] [Theory]
[MemberData(nameof(ViewLocationExpanderTestData))] [MemberData(nameof(ViewLocationExpanderTestData))]
public void FindPage_UsesViewLocationExpander_ToExpandPaths( public void FindPage_IsPartial_UsesViewLocationExpander_ToExpandPaths(
IDictionary<string, object> routeValues, IDictionary<string, object> routeValues,
IEnumerable<string> expectedSeeds) IEnumerable<string> expectedSeeds)
{ {
@ -789,7 +1022,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var context = GetActionContext(routeValues); var context = GetActionContext(routeValues);
// Act // Act
var result = viewEngine.FindPage(context, "layout"); var result = viewEngine.FindPage(context, "layout", isPartial: true);
// Assert // Assert
Assert.Equal("layout", result.Name); Assert.Equal("layout", result.Name);
@ -799,8 +1032,10 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
expander.Verify(); expander.Verify();
} }
[Fact] [Theory]
public void FindPage_ReturnsSearchedLocationsIfPageCannotBeFound() [InlineData(false)]
[InlineData(true)]
public void FindPage_ReturnsSearchedLocationsIfPageCannotBeFound(bool isPartial)
{ {
// Arrange // Arrange
var expected = new[] var expected = new[]
@ -808,13 +1043,12 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
"/Views/bar/layout.cshtml", "/Views/bar/layout.cshtml",
"/Views/Shared/layout.cshtml", "/Views/Shared/layout.cshtml",
}; };
var page = Mock.Of<IRazorPage>();
var viewEngine = CreateViewEngine(); var viewEngine = CreateViewEngine();
var context = GetActionContext(_controllerTestContext); var context = GetActionContext(_controllerTestContext);
// Act // Act
var result = viewEngine.FindPage(context, "layout"); var result = viewEngine.FindPage(context, "layout", isPartial);
// Assert // Assert
Assert.Equal("layout", result.Name); Assert.Equal("layout", result.Name);
@ -827,7 +1061,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
[InlineData(true)] [InlineData(true)]
// Looks in RouteConstraints // Looks in RouteConstraints
[InlineData(false)] [InlineData(false)]
public void FindPage_SelectsActionCaseInsensitively(bool isAttributeRouted) public void FindPage_IsPartial_SelectsActionCaseInsensitively(bool isAttributeRouted)
{ {
// The ActionDescriptor contains "Foo" and the RouteData contains "foo" // The ActionDescriptor contains "Foo" and the RouteData contains "foo"
// which matches the case of the constructor thus searching in the appropriate location. // 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" } { "controller", "foo" }
}; };
var page = new Mock<IRazorPage>(MockBehavior.Strict);
page.SetupSet(p => p.IsPartial = true);
var page = new Mock<IRazorPage>(MockBehavior.Strict).Object;
var pageFactory = new Mock<IRazorPageFactoryProvider>(); var pageFactory = new Mock<IRazorPageFactoryProvider>();
pageFactory pageFactory
.Setup(p => p.CreateFactory("/Views/Foo/details.cshtml")) .Setup(p => p.CreateFactory("/Views/Foo/details.cshtml"))
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0])) .Returns(new RazorPageFactoryResult(() => page.Object, new IChangeToken[0]))
.Verifiable(); .Verifiable();
var viewEngine = CreateViewEngine(pageFactory.Object); var viewEngine = CreateViewEngine(pageFactory.Object);
@ -850,14 +1085,17 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
{ "controller", "Foo" } { "controller", "Foo" }
}; };
var context = GetActionContextWithActionDescriptor(routeValues, routesInActionDescriptor, isAttributeRouted); var context = GetActionContextWithActionDescriptor(
routeValues,
routesInActionDescriptor,
isAttributeRouted);
// Act // Act
var result = viewEngine.FindPage(context, "details"); var result = viewEngine.FindPage(context, "details", isPartial: true);
// Assert // Assert
Assert.Equal("details", result.Name); Assert.Equal("details", result.Name);
Assert.Same(page, result.Page); Assert.Same(page.Object, result.Page);
Assert.Null(result.SearchedLocations); Assert.Null(result.SearchedLocations);
pageFactory.Verify(); pageFactory.Verify();
} }
@ -867,7 +1105,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
[InlineData(true)] [InlineData(true)]
// Looks in RouteConstraints // Looks in RouteConstraints
[InlineData(false)] [InlineData(false)]
public void FindPage_LooksForPages_UsingActionDescriptor_Controller(bool isAttributeRouted) public void FindPage_IsPartial_LooksForPages_UsingActionDescriptor_Controller(bool isAttributeRouted)
{ {
// Arrange // Arrange
var expected = new[] var expected = new[]
@ -884,13 +1122,15 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
{ {
{ "controller", "bar" } { "controller", "bar" }
}; };
var page = Mock.Of<IRazorPage>();
var viewEngine = CreateViewEngine(); var viewEngine = CreateViewEngine();
var context = GetActionContextWithActionDescriptor(routeValues, routesInActionDescriptor, isAttributeRouted); var context = GetActionContextWithActionDescriptor(
routeValues,
routesInActionDescriptor,
isAttributeRouted);
// Act // Act
var result = viewEngine.FindPage(context, "foo"); var result = viewEngine.FindPage(context, "foo", isPartial: true);
// Assert // Assert
Assert.Equal("foo", result.Name); Assert.Equal("foo", result.Name);
@ -903,7 +1143,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
[InlineData(true)] [InlineData(true)]
// Looks in RouteConstraints // Looks in RouteConstraints
[InlineData(false)] [InlineData(false)]
public void FindPage_LooksForPages_UsingActionDescriptor_Areas(bool isAttributeRouted) public void FindPage_IsPartial_LooksForPages_UsingActionDescriptor_Areas(bool isAttributeRouted)
{ {
// Arrange // Arrange
var expected = new[] var expected = new[]
@ -923,13 +1163,15 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
{ "controller", "bar" }, { "controller", "bar" },
{ "area", "world" } { "area", "world" }
}; };
var page = Mock.Of<IRazorPage>();
var viewEngine = CreateViewEngine(); var viewEngine = CreateViewEngine();
var context = GetActionContextWithActionDescriptor(routeValues, routesInActionDescriptor, isAttributeRouted); var context = GetActionContextWithActionDescriptor(
routeValues,
routesInActionDescriptor,
isAttributeRouted);
// Act // Act
var result = viewEngine.FindPage(context, "foo"); var result = viewEngine.FindPage(context, "foo", isPartial: true);
// Assert // Assert
Assert.Equal("foo", result.Name); Assert.Equal("foo", result.Name);
@ -940,7 +1182,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
[Theory] [Theory]
[InlineData(true)] [InlineData(true)]
[InlineData(false)] [InlineData(false)]
public void FindPage_LooksForPages_UsesRouteValuesAsFallback(bool isAttributeRouted) public void FindPage_IsPartial_LooksForPages_UsesRouteValuesAsFallback(bool isAttributeRouted)
{ {
// Arrange // Arrange
var expected = new[] var expected = new[]
@ -953,13 +1195,15 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
{ {
{ "controller", "foo" } { "controller", "foo" }
}; };
var page = Mock.Of<IRazorPage>();
var viewEngine = CreateViewEngine(); var viewEngine = CreateViewEngine();
var context = GetActionContextWithActionDescriptor(routeValues, new Dictionary<string, string>(), isAttributeRouted); var context = GetActionContextWithActionDescriptor(
routeValues,
new Dictionary<string, string>(),
isAttributeRouted);
// Act // Act
var result = viewEngine.FindPage(context, "bar"); var result = viewEngine.FindPage(context, "bar", isPartial: true);
// Assert // Assert
Assert.Equal("bar", result.Name); Assert.Equal("bar", result.Name);
@ -967,6 +1211,166 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
Assert.Equal(expected, result.SearchedLocations); 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<IRazorPageFactoryProvider>();
var page = Mock.Of<IRazorPage>();
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<IRazorPageFactoryProvider>();
var page = Mock.Of<IRazorPage>();
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<IRazorPageFactoryProvider>();
var page = Mock.Of<IRazorPage>();
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] [Fact]
public void AreaViewLocationFormats_ContainsExpectedLocations() 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<IRazorPageFactoryProvider>(MockBehavior.Strict);
pageFactory
.Setup(f => f.CreateFactory(It.IsAny<string>()))
.Returns(new RazorPageFactoryResult(() => Mock.Of<IRazorPage>(), new IChangeToken[0]));
return CreateViewEngine(pageFactory.Object);
}
private TestableRazorViewEngine CreateViewEngine( private TestableRazorViewEngine CreateViewEngine(
IRazorPageFactoryProvider pageFactory = null, IRazorPageFactoryProvider pageFactory = null,
IEnumerable<IViewLocationExpander> expanders = null) IEnumerable<IViewLocationExpander> expanders = null)
@ -1268,7 +1683,8 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
} }
var optionsAccessor = new Mock<IOptions<RazorViewEngineOptions>>(); var optionsAccessor = new Mock<IOptions<RazorViewEngineOptions>>();
optionsAccessor.SetupGet(v => v.Value) optionsAccessor
.SetupGet(v => v.Value)
.Returns(options); .Returns(options);
return optionsAccessor.Object; return optionsAccessor.Object;
} }

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Http.Features; using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Http.Internal; using Microsoft.AspNet.Http.Internal;
@ -79,13 +80,14 @@ namespace Microsoft.AspNet.Mvc.Razor
var viewContext = CreateViewContext(view); var viewContext = CreateViewContext(view);
var expectedWriter = viewContext.Writer; var expectedWriter = viewContext.Writer;
activator.Setup(a => a.Activate(page, It.IsAny<ViewContext>())) activator
.Callback((IRazorPage p, ViewContext c) => .Setup(a => a.Activate(page, It.IsAny<ViewContext>()))
{ .Callback((IRazorPage p, ViewContext c) =>
Assert.Same(c, viewContext); {
c.ViewData = viewData; Assert.Same(c, viewContext);
}) c.ViewData = viewData;
.Verifiable(); })
.Verifiable();
// Act // Act
await view.RenderAsync(viewContext); await view.RenderAsync(viewContext);
@ -132,8 +134,12 @@ namespace Microsoft.AspNet.Mvc.Razor
var activator = Mock.Of<IRazorPageActivator>(); var activator = Mock.Of<IRazorPageActivator>();
var viewEngine = new Mock<IRazorViewEngine>(); var viewEngine = new Mock<IRazorViewEngine>();
viewEngine.Setup(v => v.FindPage(It.IsAny<ActionContext>(), LayoutPath)) viewEngine
.Returns(new RazorPageResult(LayoutPath, layout)); .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( var view = new RazorView(
viewEngine.Object, viewEngine.Object,
activator, activator,
@ -158,8 +164,9 @@ namespace Microsoft.AspNet.Mvc.Razor
// Arrange // Arrange
var page = new TestableRazorPage(v => { }); var page = new TestableRazorPage(v => { });
var activator = new Mock<IRazorPageActivator>(); var activator = new Mock<IRazorPageActivator>();
activator.Setup(a => a.Activate(page, It.IsAny<ViewContext>())) activator
.Verifiable(); .Setup(a => a.Activate(page, It.IsAny<ViewContext>()))
.Verifiable();
var view = new RazorView( var view = new RazorView(
Mock.Of<IRazorViewEngine>(), Mock.Of<IRazorViewEngine>(),
activator.Object, activator.Object,
@ -181,9 +188,10 @@ namespace Microsoft.AspNet.Mvc.Razor
{ {
// Arrange // Arrange
var htmlEncoder = new HtmlTestEncoder(); var htmlEncoder = new HtmlTestEncoder();
var expected = string.Join(Environment.NewLine, var expected = string.Join(
"HtmlEncode[[layout-content", Environment.NewLine,
"]]HtmlEncode[[page-content]]"); "HtmlEncode[[layout-content",
"]]HtmlEncode[[page-content]]");
var page = new TestableRazorPage(v => var page = new TestableRazorPage(v =>
{ {
v.HtmlEncoder = htmlEncoder; v.HtmlEncoder = htmlEncoder;
@ -202,9 +210,10 @@ namespace Microsoft.AspNet.Mvc.Razor
.Setup(p => p.CreateFactory(LayoutPath)) .Setup(p => p.CreateFactory(LayoutPath))
.Returns(new RazorPageFactoryResult(() => layout, new IChangeToken[0])); .Returns(new RazorPageFactoryResult(() => layout, new IChangeToken[0]));
var viewEngine = new Mock<IRazorViewEngine>(); var viewEngine = new Mock<IRazorViewEngine>(MockBehavior.Strict);
viewEngine.Setup(v => v.FindPage(It.IsAny<ActionContext>(), LayoutPath)) viewEngine
.Returns(new RazorPageResult(LayoutPath, layout)); .Setup(v => v.GetPage(/*executingFilePath*/ null, LayoutPath, /*isPartial*/ true))
.Returns(new RazorPageResult(LayoutPath, layout));
var view = new RazorView( var view = new RazorView(
viewEngine.Object, viewEngine.Object,
@ -283,8 +292,9 @@ namespace Microsoft.AspNet.Mvc.Razor
v.WriteLiteral("Hello world"); v.WriteLiteral("Hello world");
}); });
var activator = new Mock<IRazorPageActivator>(); var activator = new Mock<IRazorPageActivator>();
activator.Setup(a => a.Activate(page, It.IsAny<ViewContext>())) activator
.Verifiable(); .Setup(a => a.Activate(page, It.IsAny<ViewContext>()))
.Verifiable();
var view = new RazorView( var view = new RazorView(
Mock.Of<IRazorViewEngine>(), Mock.Of<IRazorViewEngine>(),
activator.Object, activator.Object,
@ -323,14 +333,26 @@ namespace Microsoft.AspNet.Mvc.Razor
v.Layout = null; v.Layout = null;
}); });
var activator = new Mock<IRazorPageActivator>(); var activator = new Mock<IRazorPageActivator>();
activator.Setup(a => a.Activate(viewStart1, It.IsAny<ViewContext>())) activator
.Verifiable(); .Setup(a => a.Activate(viewStart1, It.IsAny<ViewContext>()))
activator.Setup(a => a.Activate(viewStart2, It.IsAny<ViewContext>())) .Verifiable();
.Verifiable(); activator
activator.Setup(a => a.Activate(page, It.IsAny<ViewContext>())) .Setup(a => a.Activate(viewStart2, It.IsAny<ViewContext>()))
.Verifiable(); .Verifiable();
activator
.Setup(a => a.Activate(page, It.IsAny<ViewContext>()))
.Verifiable();
var viewEngine = new Mock<IRazorViewEngine>(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( var view = new RazorView(
Mock.Of<IRazorViewEngine>(), viewEngine.Object,
activator.Object, activator.Object,
new[] { viewStart1, viewStart2 }, new[] { viewStart1, viewStart2 },
page, page,
@ -349,11 +371,11 @@ namespace Microsoft.AspNet.Mvc.Razor
public async Task RenderAsync_ThrowsIfLayoutPageCannotBeFound() public async Task RenderAsync_ThrowsIfLayoutPageCannotBeFound()
{ {
// Arrange // Arrange
var expected = string.Join(Environment.NewLine, var expected = string.Join(
"The layout view 'Does-Not-Exist-Layout' could not be located. " + Environment.NewLine,
"The following locations were searched:", "The layout view 'Does-Not-Exist-Layout' could not be located. The following locations were searched:",
"path1", "path1",
"path2"); "path2");
var layoutPath = "Does-Not-Exist-Layout"; var layoutPath = "Does-Not-Exist-Layout";
var page = new TestableRazorPage(v => var page = new TestableRazorPage(v =>
@ -361,18 +383,24 @@ namespace Microsoft.AspNet.Mvc.Razor
v.Layout = layoutPath; v.Layout = layoutPath;
}); });
var viewEngine = new Mock<IRazorViewEngine>(); var viewEngine = new Mock<IRazorViewEngine>(MockBehavior.Strict);
var activator = new Mock<IRazorPageActivator>(); var activator = new Mock<IRazorPageActivator>();
var view = new RazorView(viewEngine.Object, var view = new RazorView(
Mock.Of<IRazorPageActivator>(), viewEngine.Object,
new IRazorPage[0], Mock.Of<IRazorPageActivator>(),
page, new IRazorPage[0],
new HtmlTestEncoder(), page,
isPartial: false); new HtmlTestEncoder(),
isPartial: false);
var viewContext = CreateViewContext(view); var viewContext = CreateViewContext(view);
viewEngine.Setup(v => v.FindPage(viewContext, layoutPath)) viewEngine
.Returns(new RazorPageResult(layoutPath, new[] { "path1", "path2" })) .Setup(v => v.GetPage(/*executingFilePath*/ null, layoutPath, /*isPartial*/ true))
.Verifiable(); .Returns(new RazorPageResult(layoutPath, Enumerable.Empty<string>()))
.Verifiable();
viewEngine
.Setup(v => v.FindPage(viewContext, layoutPath, /*isPartial*/ true))
.Returns(new RazorPageResult(layoutPath, new[] { "path1", "path2" }))
.Verifiable();
// Act // Act
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => view.RenderAsync(viewContext)); var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => view.RenderAsync(viewContext));
@ -421,22 +449,26 @@ namespace Microsoft.AspNet.Mvc.Razor
v.Write(v.RenderSection("foot")); v.Write(v.RenderSection("foot"));
}); });
var activator = new Mock<IRazorPageActivator>(); var activator = new Mock<IRazorPageActivator>();
activator.Setup(a => a.Activate(page, It.IsAny<ViewContext>())) activator
.Verifiable(); .Setup(a => a.Activate(page, It.IsAny<ViewContext>()))
activator.Setup(a => a.Activate(layout, It.IsAny<ViewContext>())) .Verifiable();
.Verifiable(); activator
var viewEngine = new Mock<IRazorViewEngine>(); .Setup(a => a.Activate(layout, It.IsAny<ViewContext>()))
.Verifiable();
var viewEngine = new Mock<IRazorViewEngine>(MockBehavior.Strict);
viewEngine
.Setup(v => v.GetPage(/*executingFilePath*/ null, LayoutPath, /*isPartial*/ true))
.Returns(new RazorPageResult(LayoutPath, layout))
.Verifiable();
var view = new RazorView(viewEngine.Object, var view = new RazorView(
activator.Object, viewEngine.Object,
new IRazorPage[0], activator.Object,
page, new IRazorPage[0],
new HtmlTestEncoder(), page,
isPartial: false); new HtmlTestEncoder(),
isPartial: false);
var viewContext = CreateViewContext(view); var viewContext = CreateViewContext(view);
viewEngine.Setup(p => p.FindPage(viewContext, LayoutPath))
.Returns(new RazorPageResult(LayoutPath, layout))
.Verifiable();
// Act // Act
await view.RenderAsync(viewContext); await view.RenderAsync(viewContext);
@ -465,16 +497,18 @@ namespace Microsoft.AspNet.Mvc.Razor
{ {
Path = LayoutPath Path = LayoutPath
}; };
var viewEngine = new Mock<IRazorViewEngine>(); var viewEngine = new Mock<IRazorViewEngine>(MockBehavior.Strict);
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), LayoutPath)) viewEngine
.Returns(new RazorPageResult(LayoutPath, layout)); .Setup(v => v.GetPage(/*executingFilePath*/ null, LayoutPath, /*isPartial*/ true))
.Returns(new RazorPageResult(LayoutPath, layout));
var view = new RazorView(viewEngine.Object, var view = new RazorView(
Mock.Of<IRazorPageActivator>(), viewEngine.Object,
new IRazorPage[0], Mock.Of<IRazorPageActivator>(),
page, new IRazorPage[0],
new HtmlTestEncoder(), page,
isPartial: false); new HtmlTestEncoder(),
isPartial: false);
var viewContext = CreateViewContext(view); var viewContext = CreateViewContext(view);
// Act and Assert // Act and Assert
@ -526,18 +560,21 @@ namespace Microsoft.AspNet.Mvc.Razor
Path = "/Shared/Layout2.cshtml" Path = "/Shared/Layout2.cshtml"
}; };
var viewEngine = new Mock<IRazorViewEngine>(); var viewEngine = new Mock<IRazorViewEngine>(MockBehavior.Strict);
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "~/Shared/Layout1.cshtml")) viewEngine
.Returns(new RazorPageResult("~/Shared/Layout1.cshtml", nestedLayout)); .Setup(v => v.GetPage(/*executingFilePath*/ null, "~/Shared/Layout1.cshtml", /*isPartial*/ true))
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "~/Shared/Layout2.cshtml")) .Returns(new RazorPageResult("~/Shared/Layout1.cshtml", nestedLayout));
.Returns(new RazorPageResult("~/Shared/Layout2.cshtml", baseLayout)); 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, var view = new RazorView(
Mock.Of<IRazorPageActivator>(), viewEngine.Object,
new IRazorPage[0], Mock.Of<IRazorPageActivator>(),
page, new IRazorPage[0],
new HtmlTestEncoder(), page,
isPartial: false); new HtmlTestEncoder(),
isPartial: false);
var viewContext = CreateViewContext(view); var viewContext = CreateViewContext(view);
// Act // Act
@ -586,18 +623,27 @@ namespace Microsoft.AspNet.Mvc.Razor
}); });
baseLayout.Path = "Layout"; baseLayout.Path = "Layout";
var viewEngine = new Mock<IRazorViewEngine>(); var viewEngine = new Mock<IRazorViewEngine>(MockBehavior.Strict);
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "NestedLayout")) viewEngine
.Returns(new RazorPageResult("NestedLayout", nestedLayout)); .Setup(v => v.GetPage(/*executingFilePath*/ null, "NestedLayout", /*isPartial*/ true))
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "Layout")) .Returns(new RazorPageResult("NestedLayout", Enumerable.Empty<string>()));
.Returns(new RazorPageResult("Layout", baseLayout)); viewEngine
.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "NestedLayout", /*isPartial*/ true))
.Returns(new RazorPageResult("NestedLayout", nestedLayout));
viewEngine
.Setup(v => v.GetPage("NestedLayout", "Layout", /*isPartial*/ true))
.Returns(new RazorPageResult("Layout", Enumerable.Empty<string>()));
viewEngine
.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "Layout", /*isPartial*/ true))
.Returns(new RazorPageResult("Layout", baseLayout));
var view = new RazorView(viewEngine.Object, var view = new RazorView(
Mock.Of<IRazorPageActivator>(), viewEngine.Object,
new IRazorPage[0], Mock.Of<IRazorPageActivator>(),
page, new IRazorPage[0],
new HtmlTestEncoder(), page,
isPartial: false); new HtmlTestEncoder(),
isPartial: false);
var viewContext = CreateViewContext(view); var viewContext = CreateViewContext(view);
// Act // Act
@ -646,18 +692,21 @@ namespace Microsoft.AspNet.Mvc.Razor
Path = "/Shared/Layout2.cshtml" Path = "/Shared/Layout2.cshtml"
}; };
var viewEngine = new Mock<IRazorViewEngine>(); var viewEngine = new Mock<IRazorViewEngine>(MockBehavior.Strict);
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "~/Shared/Layout1.cshtml")) viewEngine
.Returns(new RazorPageResult("~/Shared/Layout1.cshtml", nestedLayout)); .Setup(v => v.GetPage(/*executingFilePath*/ null, "~/Shared/Layout1.cshtml", /*isPartial*/ true))
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "~/Shared/Layout2.cshtml")) .Returns(new RazorPageResult("~/Shared/Layout1.cshtml", nestedLayout));
.Returns(new RazorPageResult("~/Shared/Layout2.cshtml", baseLayout)); 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, var view = new RazorView(
Mock.Of<IRazorPageActivator>(), viewEngine.Object,
new IRazorPage[0], Mock.Of<IRazorPageActivator>(),
page, new IRazorPage[0],
new HtmlTestEncoder(), page,
isPartial: false); new HtmlTestEncoder(),
isPartial: false);
var viewContext = CreateViewContext(view); var viewContext = CreateViewContext(view);
// Act and Assert // Act and Assert
@ -711,18 +760,21 @@ namespace Microsoft.AspNet.Mvc.Razor
Path = "/Shared/Layout2.cshtml" Path = "/Shared/Layout2.cshtml"
}; };
var viewEngine = new Mock<IRazorViewEngine>(); var viewEngine = new Mock<IRazorViewEngine>(MockBehavior.Strict);
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "~/Shared/Layout1.cshtml")) viewEngine
.Returns(new RazorPageResult("~/Shared/Layout1.cshtml", nestedLayout)); .Setup(p => p.GetPage("Page", "~/Shared/Layout1.cshtml", /*isPartial*/ true))
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "~/Shared/Layout2.cshtml")) .Returns(new RazorPageResult("~/Shared/Layout1.cshtml", nestedLayout));
.Returns(new RazorPageResult("~/Shared/Layout2.cshtml", baseLayout)); 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, var view = new RazorView(
Mock.Of<IRazorPageActivator>(), viewEngine.Object,
new IRazorPage[0], Mock.Of<IRazorPageActivator>(),
page, new IRazorPage[0],
new HtmlTestEncoder(), page,
isPartial: false); new HtmlTestEncoder(),
isPartial: false);
var viewContext = CreateViewContext(view); var viewContext = CreateViewContext(view);
// Act and Assert // Act and Assert
@ -745,16 +797,18 @@ namespace Microsoft.AspNet.Mvc.Razor
{ {
Path = LayoutPath Path = LayoutPath
}; };
var viewEngine = new Mock<IRazorViewEngine>(); var viewEngine = new Mock<IRazorViewEngine>(MockBehavior.Strict);
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), LayoutPath)) viewEngine
.Returns(new RazorPageResult(LayoutPath, layout)); .Setup(p => p.GetPage(/*executingFilePath*/ null, LayoutPath, /*isPartial*/ true))
.Returns(new RazorPageResult(LayoutPath, layout));
var view = new RazorView(viewEngine.Object, var view = new RazorView(
Mock.Of<IRazorPageActivator>(), viewEngine.Object,
new IRazorPage[0], Mock.Of<IRazorPageActivator>(),
page, new IRazorPage[0],
new HtmlTestEncoder(), page,
isPartial: false); new HtmlTestEncoder(),
isPartial: false);
var viewContext = CreateViewContext(view); var viewContext = CreateViewContext(view);
// Act and Assert // Act and Assert
@ -807,18 +861,95 @@ namespace Microsoft.AspNet.Mvc.Razor
}); });
layout2.Path = "~/Shared/Layout2.cshtml"; layout2.Path = "~/Shared/Layout2.cshtml";
var viewEngine = new Mock<IRazorViewEngine>(); var viewEngine = new Mock<IRazorViewEngine>(MockBehavior.Strict);
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "~/Shared/Layout1.cshtml")) viewEngine
.Returns(new RazorPageResult("~/Shared/Layout1.cshtml", layout1)); .Setup(p => p.GetPage(/*executingFilePath*/ null, "~/Shared/Layout1.cshtml", /*isPartial*/ true))
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "~/Shared/Layout2.cshtml")) .Returns(new RazorPageResult("~/Shared/Layout1.cshtml", layout1));
.Returns(new RazorPageResult("~/Shared/Layout2.cshtml", layout2)); 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, var view = new RazorView(
Mock.Of<IRazorPageActivator>(), viewEngine.Object,
new IRazorPage[0], Mock.Of<IRazorPageActivator>(),
page, new IRazorPage[0],
new HtmlTestEncoder(), page,
isPartial: false); 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<IRazorViewEngine>(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<IRazorPageActivator>(),
new IRazorPage[0],
page,
new HtmlTestEncoder(),
isPartial: false);
var viewContext = CreateViewContext(view); var viewContext = CreateViewContext(view);
// Act // Act
@ -845,16 +976,21 @@ namespace Microsoft.AspNet.Mvc.Razor
}); });
layout.Path = "Shared/Layout.cshtml"; layout.Path = "Shared/Layout.cshtml";
var viewEngine = new Mock<IRazorViewEngine>(); var viewEngine = new Mock<IRazorViewEngine>(MockBehavior.Strict);
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "_Layout")) viewEngine
.Returns(new RazorPageResult("_Layout", layout)); .Setup(p => p.GetPage(It.IsAny<string>(), "_Layout", /*isPartial*/ true))
.Returns(new RazorPageResult("_Layout", Enumerable.Empty<string>()));
viewEngine
.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "_Layout", /*isPartial*/ true))
.Returns(new RazorPageResult("_Layout", layout));
var view = new RazorView(viewEngine.Object, var view = new RazorView(
Mock.Of<IRazorPageActivator>(), viewEngine.Object,
new IRazorPage[0], Mock.Of<IRazorPageActivator>(),
page, new IRazorPage[0],
new HtmlTestEncoder(), page,
isPartial: false); new HtmlTestEncoder(),
isPartial: false);
var viewContext = CreateViewContext(view); var viewContext = CreateViewContext(view);
// Act and Assert // Act and Assert
@ -888,18 +1024,27 @@ namespace Microsoft.AspNet.Mvc.Razor
}); });
layout2.Path = "/Shared/Layout2.cshtml"; layout2.Path = "/Shared/Layout2.cshtml";
var viewEngine = new Mock<IRazorViewEngine>(); var viewEngine = new Mock<IRazorViewEngine>(MockBehavior.Strict);
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "_Layout")) viewEngine
.Returns(new RazorPageResult("_Layout", layout1)); .Setup(p => p.GetPage(It.IsAny<string>(), "_Layout", /*isPartial*/ true))
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "_Layout2")) .Returns(new RazorPageResult("_Layout1", Enumerable.Empty<string>()));
.Returns(new RazorPageResult("_Layout2", layout2)); viewEngine
.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "_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<string>()));
viewEngine
.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "_Layout2", /*isPartial*/ true))
.Returns(new RazorPageResult("_Layout2", layout2));
var view = new RazorView(viewEngine.Object, var view = new RazorView(
Mock.Of<IRazorPageActivator>(), viewEngine.Object,
new IRazorPage[0], Mock.Of<IRazorPageActivator>(),
page, new IRazorPage[0],
new HtmlTestEncoder(), page,
isPartial: false); new HtmlTestEncoder(),
isPartial: false);
var viewContext = CreateViewContext(view); var viewContext = CreateViewContext(view);
// Act and Assert // Act and Assert
@ -944,7 +1089,7 @@ namespace Microsoft.AspNet.Mvc.Razor
await writer.WriteLineAsync(htmlEncoder.Encode(v.RenderSection("foo").ToString())); await writer.WriteLineAsync(htmlEncoder.Encode(v.RenderSection("foo").ToString()));
}); });
}); });
nestedLayout.Path = "~/Shared/Layout2.cshtml"; nestedLayout.Path = "~/Shared/Layout1.cshtml";
var baseLayout = new TestableRazorPage(v => var baseLayout = new TestableRazorPage(v =>
{ {
@ -953,20 +1098,23 @@ namespace Microsoft.AspNet.Mvc.Razor
v.RenderBodyPublic(); v.RenderBodyPublic();
v.Write(v.RenderSection("foo")); v.Write(v.RenderSection("foo"));
}); });
baseLayout.Path = "~/Shared/Layout1.cshtml"; baseLayout.Path = "~/Shared/Layout2.cshtml";
var viewEngine = new Mock<IRazorViewEngine>(); var viewEngine = new Mock<IRazorViewEngine>(MockBehavior.Strict);
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "~/Shared/Layout1.cshtml")) viewEngine
.Returns(new RazorPageResult("~/Shared/Layout1.cshtml", nestedLayout)); .Setup(p => p.GetPage(/*executingFilePath*/ null, "~/Shared/Layout1.cshtml", /*isPartial*/ true))
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "~/Shared/Layout2.cshtml")) .Returns(new RazorPageResult("~/Shared/Layout1.cshtml", nestedLayout));
.Returns(new RazorPageResult("~/Shared/Layout2.cshtml", baseLayout)); 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, var view = new RazorView(
Mock.Of<IRazorPageActivator>(), viewEngine.Object,
new IRazorPage[0], Mock.Of<IRazorPageActivator>(),
page, new IRazorPage[0],
new HtmlTestEncoder(), page,
isPartial: false); new HtmlTestEncoder(),
isPartial: false);
var viewContext = CreateViewContext(view); var viewContext = CreateViewContext(view);
// Act // Act
@ -1010,16 +1158,21 @@ namespace Microsoft.AspNet.Mvc.Razor
v.Write(v.RenderSection("foo")); v.Write(v.RenderSection("foo"));
}); });
var viewEngine = new Mock<IRazorViewEngine>(); var viewEngine = new Mock<IRazorViewEngine>(MockBehavior.Strict);
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "layout-1")) viewEngine
.Returns(new RazorPageResult("layout-1", layout1)); .Setup(p => p.GetPage(/*executingFilePath*/ null, "layout-1", /*isPartial*/ true))
.Returns(new RazorPageResult("layout-1", Enumerable.Empty<string>()));
viewEngine
.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "layout-1", /*isPartial*/ true))
.Returns(new RazorPageResult("layout-1", layout1));
var view = new RazorView(viewEngine.Object, var view = new RazorView(
Mock.Of<IRazorPageActivator>(), viewEngine.Object,
new IRazorPage[0], Mock.Of<IRazorPageActivator>(),
page, new IRazorPage[0],
new HtmlTestEncoder(), page,
isPartial: false); new HtmlTestEncoder(),
isPartial: false);
var viewContext = CreateViewContext(view); var viewContext = CreateViewContext(view);
// Act // Act
@ -1060,16 +1213,21 @@ namespace Microsoft.AspNet.Mvc.Razor
v.Write(v.RenderSection("foo")); v.Write(v.RenderSection("foo"));
}); });
var viewEngine = new Mock<IRazorViewEngine>(); var viewEngine = new Mock<IRazorViewEngine>(MockBehavior.Strict);
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "layout-1")) viewEngine
.Returns(new RazorPageResult("layout-1", layout1)); .Setup(p => p.GetPage(/*executingFilePath*/ null, "layout-1", /*isPartial*/ true))
.Returns(new RazorPageResult("layout-1", Enumerable.Empty<string>()));
viewEngine
.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "layout-1", /*isPartial*/ true))
.Returns(new RazorPageResult("layout-1", layout1));
var view = new RazorView(viewEngine.Object, var view = new RazorView(
Mock.Of<IRazorPageActivator>(), viewEngine.Object,
new IRazorPage[0], Mock.Of<IRazorPageActivator>(),
page, new IRazorPage[0],
new HtmlTestEncoder(), page,
isPartial: false); new HtmlTestEncoder(),
isPartial: false);
var viewContext = CreateViewContext(view); var viewContext = CreateViewContext(view);
// Act // Act
@ -1094,12 +1252,13 @@ namespace Microsoft.AspNet.Mvc.Razor
v.WriteLiteral("after-flush"); v.WriteLiteral("after-flush");
}); });
var view = new RazorView(Mock.Of<IRazorViewEngine>(), var view = new RazorView(
Mock.Of<IRazorPageActivator>(), Mock.Of<IRazorViewEngine>(),
new IRazorPage[0], Mock.Of<IRazorPageActivator>(),
page, new IRazorPage[0],
new HtmlTestEncoder(), page,
isPartial: false); new HtmlTestEncoder(),
isPartial: false);
var viewContext = CreateViewContext(view); var viewContext = CreateViewContext(view);
// Act and Assert // Act and Assert
@ -1134,17 +1293,19 @@ namespace Microsoft.AspNet.Mvc.Razor
v.RenderBodyPublic(); v.RenderBodyPublic();
v.Layout = "~/Shared/Layout2.cshtml"; v.Layout = "~/Shared/Layout2.cshtml";
}); });
var viewEngine = new Mock<IRazorViewEngine>(); var viewEngine = new Mock<IRazorViewEngine>(MockBehavior.Strict);
var layoutPath = "~/Shared/Layout1.cshtml"; var layoutPath = "~/Shared/Layout1.cshtml";
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), layoutPath)) viewEngine
.Returns(new RazorPageResult(layoutPath, layoutPage)); .Setup(p => p.GetPage("/Views/TestPath/Test.cshtml", layoutPath, /*isPartial*/ true))
.Returns(new RazorPageResult(layoutPath, layoutPage));
var view = new RazorView(viewEngine.Object, var view = new RazorView(
Mock.Of<IRazorPageActivator>(), viewEngine.Object,
new IRazorPage[0], Mock.Of<IRazorPageActivator>(),
page, new IRazorPage[0],
new HtmlTestEncoder(), page,
isPartial: false); new HtmlTestEncoder(),
isPartial: false);
var viewContext = CreateViewContext(view); var viewContext = CreateViewContext(view);
// Act and Assert // Act and Assert
@ -1162,31 +1323,34 @@ namespace Microsoft.AspNet.Mvc.Razor
var layoutExecuted = false; var layoutExecuted = false;
var count = -1; var count = -1;
var feature = new Mock<IPageExecutionListenerFeature>(MockBehavior.Strict); var feature = new Mock<IPageExecutionListenerFeature>(MockBehavior.Strict);
feature.Setup(f => f.DecorateWriter(It.IsAny<RazorTextWriter>())) feature
.Returns(() => .Setup(f => f.DecorateWriter(It.IsAny<RazorTextWriter>()))
{ .Returns(() =>
count++; {
if (count == 0) count++;
{ if (count == 0)
return pageWriter; {
} return pageWriter;
else if (count == 1) }
{ else if (count == 1)
return layoutWriter; {
} return layoutWriter;
throw new Exception(); }
}) throw new Exception();
.Verifiable(); })
.Verifiable();
var pageContext = Mock.Of<IPageExecutionContext>(); var pageContext = Mock.Of<IPageExecutionContext>();
feature.Setup(f => f.GetContext("/MyPage.cshtml", pageWriter)) feature
.Returns(pageContext) .Setup(f => f.GetContext("/MyPage.cshtml", pageWriter))
.Verifiable(); .Returns(pageContext)
.Verifiable();
var layoutContext = Mock.Of<IPageExecutionContext>(); var layoutContext = Mock.Of<IPageExecutionContext>();
feature.Setup(f => f.GetContext("/Layout.cshtml", layoutWriter)) feature
.Returns(layoutContext) .Setup(f => f.GetContext("/Layout.cshtml", layoutWriter))
.Verifiable(); .Returns(layoutContext)
.Verifiable();
var page = new TestableRazorPage(v => var page = new TestableRazorPage(v =>
{ {
@ -1208,15 +1372,21 @@ namespace Microsoft.AspNet.Mvc.Razor
}); });
layout.Path = "/Layout.cshtml"; layout.Path = "/Layout.cshtml";
var viewEngine = new Mock<IRazorViewEngine>(); var viewEngine = new Mock<IRazorViewEngine>(MockBehavior.Strict);
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "Layout")) viewEngine
.Returns(new RazorPageResult("Layout", layout)); .Setup(p => p.GetPage("/MyPage.cshtml", "Layout", /*isPartial*/ true))
var view = new RazorView(viewEngine.Object, .Returns(new RazorPageResult("Layout", Enumerable.Empty<string>()));
Mock.Of<IRazorPageActivator>(), viewEngine
new IRazorPage[0], .Setup(p => p.FindPage(It.IsAny<ActionContext>(), "Layout", /*isPartial*/ true))
page, .Returns(new RazorPageResult("/Layout.cshtml", layout));
new HtmlTestEncoder(),
isPartial: false); var view = new RazorView(
viewEngine.Object,
Mock.Of<IRazorPageActivator>(),
new IRazorPage[0],
page,
new HtmlTestEncoder(),
isPartial: false);
var viewContext = CreateViewContext(view); var viewContext = CreateViewContext(view);
viewContext.HttpContext.Features.Set<IPageExecutionListenerFeature>(feature.Object); viewContext.HttpContext.Features.Set<IPageExecutionListenerFeature>(feature.Object);
@ -1256,12 +1426,13 @@ namespace Microsoft.AspNet.Mvc.Razor
}); });
page.Path = "/MyPartialPage.cshtml"; page.Path = "/MyPartialPage.cshtml";
var view = new RazorView(Mock.Of<IRazorViewEngine>(), var view = new RazorView(
Mock.Of<IRazorPageActivator>(), Mock.Of<IRazorViewEngine>(),
new IRazorPage[0], Mock.Of<IRazorPageActivator>(),
page, new IRazorPage[0],
new HtmlTestEncoder(), page,
isPartial: true); new HtmlTestEncoder(),
isPartial: true);
var viewContext = CreateViewContext(view); var viewContext = CreateViewContext(view);
viewContext.Writer = writer; viewContext.Writer = writer;
viewContext.HttpContext.Features.Set<IPageExecutionListenerFeature>(feature.Object); viewContext.HttpContext.Features.Set<IPageExecutionListenerFeature>(feature.Object);
@ -1288,12 +1459,13 @@ namespace Microsoft.AspNet.Mvc.Razor
executed = true; executed = true;
}); });
var view = new RazorView(Mock.Of<IRazorViewEngine>(), var view = new RazorView
Mock.Of<IRazorPageActivator>(), (Mock.Of<IRazorViewEngine>(),
new IRazorPage[0], Mock.Of<IRazorPageActivator>(),
page, new IRazorPage[0],
new HtmlTestEncoder(), page,
isPartial); new HtmlTestEncoder(),
isPartial);
var viewContext = CreateViewContext(view); var viewContext = CreateViewContext(view);
// Act // Act
@ -1326,14 +1498,79 @@ namespace Microsoft.AspNet.Mvc.Razor
actualViewStart = v.Layout; actualViewStart = v.Layout;
v.Layout = expectedPage; v.Layout = expectedPage;
}); });
var viewEngine = Mock.Of<IRazorViewEngine>(); var viewEngine = new Mock<IRazorViewEngine>(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, var view = new RazorView(
Mock.Of<IRazorPageActivator>(), viewEngine.Object,
new[] { viewStart1, viewStart2 }, Mock.Of<IRazorPageActivator>(),
page, new[] { viewStart1, viewStart2 },
new HtmlTestEncoder(), page,
isPartial: false); 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<IRazorViewEngine>(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<IRazorPageActivator>(),
new[] { viewStart1, viewStart2 },
page,
new HtmlTestEncoder(),
isPartial: false);
var viewContext = CreateViewContext(view); var viewContext = CreateViewContext(view);
// Act // Act
@ -1364,10 +1601,16 @@ namespace Microsoft.AspNet.Mvc.Razor
actual = v.Layout; actual = v.Layout;
v.Layout = null; v.Layout = null;
}); });
var viewEngine = Mock.Of<IRazorViewEngine>(); var viewEngine = new Mock<IRazorViewEngine>(MockBehavior.Strict);
viewEngine
.Setup(engine => engine.MakePathAbsolute(/*executingFilePath*/ null, "Layout"))
.Returns("Layout");
viewEngine
.Setup(engine => engine.MakePathAbsolute(/*executingFilePath*/ null, /*pagePath*/ null))
.Returns<string>(null);
var view = new RazorView( var view = new RazorView(
viewEngine, viewEngine.Object,
Mock.Of<IRazorPageActivator>(), Mock.Of<IRazorPageActivator>(),
new[] { viewStart1, viewStart2 }, new[] { viewStart1, viewStart2 },
page, page,
@ -1404,10 +1647,13 @@ namespace Microsoft.AspNet.Mvc.Razor
isPartialLayout = v.IsPartial; isPartialLayout = v.IsPartial;
v.RenderBodyPublic(); v.RenderBodyPublic();
}); });
var viewEngine = new Mock<IRazorViewEngine>(); var viewEngine = new Mock<IRazorViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "/Layout.cshtml")) .Setup(p => p.MakePathAbsolute(/*executingFilePath*/ null, "/Layout.cshtml"))
.Returns(new RazorPageResult("Layout", layout)); .Returns("/Layout.cshtml");
viewEngine
.Setup(p => p.GetPage(/*executingFilePath*/ null, "/Layout.cshtml", /*isPartial*/ true))
.Returns(new RazorPageResult("/Layout.cshtml", layout));
var view = new RazorView( var view = new RazorView(
viewEngine.Object, viewEngine.Object,
@ -1436,12 +1682,13 @@ namespace Microsoft.AspNet.Mvc.Razor
{ {
isPartialPage = v.IsPartial; isPartialPage = v.IsPartial;
}); });
var view = new RazorView(Mock.Of<IRazorViewEngine>(), var view = new RazorView(
Mock.Of<IRazorPageActivator>(), Mock.Of<IRazorViewEngine>(),
new IRazorPage[0], Mock.Of<IRazorPageActivator>(),
page, new IRazorPage[0],
new HtmlTestEncoder(), page,
isPartial: true); new HtmlTestEncoder(),
isPartial: true);
var viewContext = CreateViewContext(view); var viewContext = CreateViewContext(view);
// Act // Act

View File

@ -4,6 +4,7 @@
#if MOCK_SUPPORT #if MOCK_SUPPORT
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Http; using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Internal; using Microsoft.AspNet.Http.Internal;
@ -36,9 +37,13 @@ namespace Microsoft.AspNet.Mvc
var actionContext = GetActionContext(); var actionContext = GetActionContext();
var viewEngine = new Mock<IViewEngine>(); var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(v => v.GetView(/*executingFilePath*/ null, "MyView", /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound("MyView", Enumerable.Empty<string>()))
.Verifiable();
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), "MyView", /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound("MyView", new[] { "Location1", "Location2" })) .Returns(ViewEngineResult.NotFound("MyView", new[] { "Location1", "Location2" }))
.Verifiable(); .Verifiable();
@ -82,7 +87,11 @@ namespace Microsoft.AspNet.Mvc
var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict); var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(e => e.FindPartialView(context, "myview")) .Setup(v => v.GetView(/*executingFilePath*/ null, "myview", /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound("myview", Enumerable.Empty<string>()))
.Verifiable();
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), "myview", /*isPartial*/ true))
.Returns(ViewEngineResult.Found("myview", view.Object)) .Returns(ViewEngineResult.Found("myview", view.Object))
.Verifiable(); .Verifiable();

View File

@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Antiforgery; using Microsoft.AspNet.Antiforgery;
@ -315,10 +316,15 @@ namespace Microsoft.AspNet.Mvc.Rendering
}) })
.Returns(Task.FromResult(0)); .Returns(Task.FromResult(0));
var viewEngine = new Mock<ICompositeViewEngine>(); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.Found("MyView", view.Object)); .Returns(ViewEngineResult.NotFound("MyView", Enumerable.Empty<string>()))
.Verifiable();
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.Found("MyView", view.Object))
.Verifiable();
return viewEngine.Object; return viewEngine.Object;
} }

View File

@ -3,6 +3,7 @@
#if MOCK_SUPPORT #if MOCK_SUPPORT
using System.IO; using System.IO;
using System.Text;
using Microsoft.AspNet.Http.Internal; using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Abstractions; using Microsoft.AspNet.Mvc.Abstractions;
using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.ModelBinding;
@ -42,6 +43,42 @@ namespace Microsoft.AspNet.Mvc.Rendering
Assert.Equal("property", context.ViewBag.Another); Assert.Equal("property", context.ViewBag.Another);
Assert.Equal("property", context.ViewData["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<IView>(),
viewData: new ViewDataDictionary(metadataProvider: new EmptyModelMetadataProvider()),
tempData: new TempDataDictionary(new HttpContextAccessor(), Mock.Of<ITempDataProvider>()),
writer: TextWriter.Null,
htmlHelperOptions: new HtmlHelperOptions());
var view = Mock.Of<IView>();
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 #endif

View File

@ -5,6 +5,7 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Http.Internal; using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Abstractions; using Microsoft.AspNet.Mvc.Abstractions;
@ -35,9 +36,14 @@ namespace Microsoft.AspNet.Mvc
.Verifiable(); .Verifiable();
var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict); var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict);
viewEngine.Setup(e => e.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) viewEngine
.Returns(ViewEngineResult.Found("some-view", view.Object)) .Setup(v => v.GetView(/*executingFilePath*/ null, "some-view", /*isPartial*/ true))
.Verifiable(); .Returns(ViewEngineResult.NotFound("some-view", Enumerable.Empty<string>()))
.Verifiable();
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), "Components/Invoke/some-view", /*isPartial*/ true))
.Returns(ViewEngineResult.Found("Components/Invoke/some-view", view.Object))
.Verifiable();
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider()); var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider());
@ -69,9 +75,10 @@ namespace Microsoft.AspNet.Mvc
.Verifiable(); .Verifiable();
var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict); var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict);
viewEngine.Setup(e => e.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) viewEngine
.Returns(ViewEngineResult.Found("Default", view.Object)) .Setup(v => v.FindView(It.IsAny<ActionContext>(), "Components/Invoke/Default", /*isPartial*/ true))
.Verifiable(); .Returns(ViewEngineResult.Found("Components/Invoke/Default", view.Object))
.Verifiable();
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider()); var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider());
@ -102,9 +109,10 @@ namespace Microsoft.AspNet.Mvc
.Verifiable(); .Verifiable();
var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict); var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict);
viewEngine.Setup(e => e.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) viewEngine
.Returns(ViewEngineResult.Found("Default", view.Object)) .Setup(v => v.FindView(It.IsAny<ActionContext>(), "Components/Invoke/Default", /*isPartial*/ true))
.Verifiable(); .Returns(ViewEngineResult.Found("Components/Invoke/Default", view.Object))
.Verifiable();
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider()); var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider());
@ -139,16 +147,21 @@ namespace Microsoft.AspNet.Mvc
{ {
// Arrange // Arrange
var expected = string.Join(Environment.NewLine, var expected = string.Join(Environment.NewLine,
"The view 'Components/Object/some-view' was not found. The following locations were searched:", "The view 'Components/Invoke/some-view' was not found. The following locations were searched:",
"location1", "location1",
"location2."); "location2.");
var view = Mock.Of<IView>(); var view = Mock.Of<IView>();
var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict); var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict);
viewEngine.Setup(e => e.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) viewEngine
.Returns(ViewEngineResult.NotFound("Components/Object/some-view", new[] { "location1", "location2" })) .Setup(v => v.GetView(/*executingFilePath*/ null, "some-view", /*isPartial*/ true))
.Verifiable(); .Returns(ViewEngineResult.NotFound("some-view", Enumerable.Empty<string>()))
.Verifiable();
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), "Components/Invoke/some-view", /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound("Components/Invoke/some-view", new[] { "location1", "location2" }))
.Verifiable();
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider()); var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider());
@ -179,9 +192,14 @@ namespace Microsoft.AspNet.Mvc
.Verifiable(); .Verifiable();
var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict); var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict);
viewEngine.Setup(e => e.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) viewEngine
.Returns(ViewEngineResult.Found("some-view", view.Object)) .Setup(v => v.GetView(/*executingFilePath*/ null, "some-view", /*isPartial*/ true))
.Verifiable(); .Returns(ViewEngineResult.NotFound("some-view", Enumerable.Empty<string>()))
.Verifiable();
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), "Components/Invoke/some-view", /*isPartial*/ true))
.Returns(ViewEngineResult.Found("Components/Invoke/some-view", view.Object))
.Verifiable();
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider()); var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider());
@ -210,9 +228,14 @@ namespace Microsoft.AspNet.Mvc
var view = Mock.Of<IView>(); var view = Mock.Of<IView>();
var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict); var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict);
viewEngine.Setup(e => e.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) viewEngine
.Returns(ViewEngineResult.Found("some-view", view)) .Setup(v => v.GetView(/*executingFilePath*/ null, "some-view", /*isPartial*/ true))
.Verifiable(); .Returns(ViewEngineResult.NotFound("some-view", Enumerable.Empty<string>()))
.Verifiable();
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), "Components/Invoke/some-view", /*isPartial*/ true))
.Returns(ViewEngineResult.Found("Components/Invoke/some-view", view))
.Verifiable();
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider()); var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider());
@ -240,9 +263,14 @@ namespace Microsoft.AspNet.Mvc
var view = Mock.Of<IView>(); var view = Mock.Of<IView>();
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine.Setup(e => e.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) viewEngine
.Returns(ViewEngineResult.Found("some-view", view)) .Setup(v => v.GetView(/*executingFilePath*/ null, "some-view", /*isPartial*/ true))
.Verifiable(); .Returns(ViewEngineResult.NotFound("some-view", Enumerable.Empty<string>()))
.Verifiable();
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), "Components/Invoke/some-view", /*isPartial*/ true))
.Returns(ViewEngineResult.Found("Components/Invoke/some-view", view))
.Verifiable();
var serviceProvider = new Mock<IServiceProvider>(); var serviceProvider = new Mock<IServiceProvider>();
serviceProvider.Setup(p => p.GetService(typeof(ICompositeViewEngine))) serviceProvider.Setup(p => p.GetService(typeof(ICompositeViewEngine)))
@ -275,18 +303,23 @@ namespace Microsoft.AspNet.Mvc
{ {
// Arrange // Arrange
var expected = string.Join(Environment.NewLine, var expected = string.Join(Environment.NewLine,
"The view 'Components/Object/some-view' was not found. The following locations were searched:", "The view 'Components/Invoke/some-view' was not found. The following locations were searched:",
"view-location1", "view-location1",
"view-location2."); "view-location2.");
var view = Mock.Of<IView>(); var view = Mock.Of<IView>();
var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict); var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict);
viewEngine.Setup(e => e.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) viewEngine
.Returns(ViewEngineResult.NotFound( .Setup(v => v.GetView(/*executingFilePath*/ null, "some-view", /*isPartial*/ true))
"Components/Object/some-view", .Returns(ViewEngineResult.NotFound("some-view", Enumerable.Empty<string>()))
new[] { "view-location1", "view-location2" })) .Verifiable();
.Verifiable(); viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), "Components/Invoke/some-view", /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound(
"Components/Invoke/some-view",
new[] { "view-location1", "view-location2" }))
.Verifiable();
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider()); var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider());
@ -338,7 +371,7 @@ namespace Microsoft.AspNet.Mvc
[Theory] [Theory]
[InlineData(null)] [InlineData(null)]
[InlineData("")] [InlineData("")]
public void Execute_CallsFindPartialView_WithExpectedPath_WhenViewNameIsNullOrEmpty(string viewName) public void Execute_CallsFindView_WithIsPartialAndExpectedPath_WhenViewNameIsNullOrEmpty(string viewName)
{ {
// Arrange // Arrange
var shortName = "SomeShortName"; var shortName = "SomeShortName";
@ -348,8 +381,8 @@ namespace Microsoft.AspNet.Mvc
var expectedViewName = $"Components/{shortName}/Default"; var expectedViewName = $"Components/{shortName}/Default";
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), expectedViewName)) .Setup(v => v.FindView(It.IsAny<ActionContext>(), expectedViewName, /*isPartial*/ true))
.Returns(ViewEngineResult.Found(string.Empty, new Mock<IView>().Object)) .Returns(ViewEngineResult.Found(expectedViewName, new Mock<IView>().Object))
.Verifiable(); .Verifiable();
var componentResult = new ViewViewComponentResult(); var componentResult = new ViewViewComponentResult();
@ -367,13 +400,13 @@ namespace Microsoft.AspNet.Mvc
[InlineData("~/Home/Index/MyViewComponent1.cshtml")] [InlineData("~/Home/Index/MyViewComponent1.cshtml")]
[InlineData("~MyViewComponent2.cshtml")] [InlineData("~MyViewComponent2.cshtml")]
[InlineData("/MyViewComponent3.cshtml")] [InlineData("/MyViewComponent3.cshtml")]
public void Execute_CallsFindPartialView_WithExpectedPath_WhenViewNameIsSpecified(string viewName) public void Execute_CallsFindView_WithIsPartialAndExpectedPath_WhenViewNameIsSpecified(string viewName)
{ {
// Arrange // Arrange
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), viewName)) .Setup(v => v.GetView(/*executingFilePath*/ null, viewName, /*isPartial*/ true))
.Returns(ViewEngineResult.Found(string.Empty, new Mock<IView>().Object)) .Returns(ViewEngineResult.Found(viewName, new Mock<IView>().Object))
.Verifiable(); .Verifiable();
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider()); var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider());
var componentContext = GetViewComponentContext(new Mock<IView>().Object, viewData); var componentContext = GetViewComponentContext(new Mock<IView>().Object, viewData);
@ -417,6 +450,7 @@ namespace Microsoft.AspNet.Mvc
var viewComponentDescriptor = new ViewComponentDescriptor() var viewComponentDescriptor = new ViewComponentDescriptor()
{ {
ShortName = "Invoke",
Type = typeof(object), Type = typeof(object),
}; };

View File

@ -42,7 +42,7 @@ namespace Microsoft.AspNet.Mvc.ViewEngines
var compositeViewEngine = new CompositeViewEngine(optionsAccessor); var compositeViewEngine = new CompositeViewEngine(optionsAccessor);
// Act // Act
var result = compositeViewEngine.FindView(actionContext, viewName); var result = compositeViewEngine.FindView(actionContext, viewName, isPartial: false);
// Assert // Assert
Assert.False(result.Success); Assert.False(result.Success);
@ -55,15 +55,16 @@ namespace Microsoft.AspNet.Mvc.ViewEngines
{ {
// Arrange // Arrange
var viewName = "test-view"; var viewName = "test-view";
var engine = new Mock<IViewEngine>(); var engine = new Mock<IViewEngine>(MockBehavior.Strict);
engine.Setup(e => e.FindView(It.IsAny<ActionContext>(), It.IsAny<string>())) engine
.Returns(ViewEngineResult.NotFound(viewName, new[] { "controller/test-view" })); .Setup(e => e.FindView(It.IsAny<ActionContext>(), viewName, /*isPartial*/ false))
.Returns(ViewEngineResult.NotFound(viewName, new[] { "controller/test-view" }));
var optionsAccessor = new TestOptionsManager<MvcViewOptions>(); var optionsAccessor = new TestOptionsManager<MvcViewOptions>();
optionsAccessor.Value.ViewEngines.Add(engine.Object); optionsAccessor.Value.ViewEngines.Add(engine.Object);
var compositeViewEngine = new CompositeViewEngine(optionsAccessor); var compositeViewEngine = new CompositeViewEngine(optionsAccessor);
// Act // Act
var result = compositeViewEngine.FindView(GetActionContext(), viewName); var result = compositeViewEngine.FindView(GetActionContext(), viewName, isPartial: false);
// Assert // Assert
Assert.False(result.Success); Assert.False(result.Success);
@ -75,16 +76,17 @@ namespace Microsoft.AspNet.Mvc.ViewEngines
{ {
// Arrange // Arrange
var viewName = "test-view"; var viewName = "test-view";
var engine = new Mock<IViewEngine>(); var engine = new Mock<IViewEngine>(MockBehavior.Strict);
var view = Mock.Of<IView>(); var view = Mock.Of<IView>();
engine.Setup(e => e.FindView(It.IsAny<ActionContext>(), It.IsAny<string>())) engine
.Returns(ViewEngineResult.Found(viewName, view)); .Setup(e => e.FindView(It.IsAny<ActionContext>(), viewName, /*isPartial*/ false))
.Returns(ViewEngineResult.Found(viewName, view));
var optionsAccessor = new TestOptionsManager<MvcViewOptions>(); var optionsAccessor = new TestOptionsManager<MvcViewOptions>();
optionsAccessor.Value.ViewEngines.Add(engine.Object); optionsAccessor.Value.ViewEngines.Add(engine.Object);
var compositeViewEngine = new CompositeViewEngine(optionsAccessor); var compositeViewEngine = new CompositeViewEngine(optionsAccessor);
// Act // Act
var result = compositeViewEngine.FindView(GetActionContext(), viewName); var result = compositeViewEngine.FindView(GetActionContext(), viewName, isPartial: false);
// Assert // Assert
Assert.True(result.Success); Assert.True(result.Success);
@ -96,17 +98,20 @@ namespace Microsoft.AspNet.Mvc.ViewEngines
{ {
// Arrange // Arrange
var viewName = "foo"; var viewName = "foo";
var engine1 = new Mock<IViewEngine>(); var engine1 = new Mock<IViewEngine>(MockBehavior.Strict);
var engine2 = new Mock<IViewEngine>(); var engine2 = new Mock<IViewEngine>(MockBehavior.Strict);
var engine3 = new Mock<IViewEngine>(); var engine3 = new Mock<IViewEngine>(MockBehavior.Strict);
var view2 = Mock.Of<IView>(); var view2 = Mock.Of<IView>();
var view3 = Mock.Of<IView>(); var view3 = Mock.Of<IView>();
engine1.Setup(e => e.FindView(It.IsAny<ActionContext>(), It.IsAny<string>())) engine1
.Returns(ViewEngineResult.NotFound(viewName, Enumerable.Empty<string>())); .Setup(e => e.FindView(It.IsAny<ActionContext>(), viewName, /*isPartial*/ false))
engine2.Setup(e => e.FindView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Returns(ViewEngineResult.NotFound(viewName, Enumerable.Empty<string>()));
.Returns(ViewEngineResult.Found(viewName, view2)); engine2
engine3.Setup(e => e.FindView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(e => e.FindView(It.IsAny<ActionContext>(), viewName, /*isPartial*/ false))
.Returns(ViewEngineResult.Found(viewName, view3)); .Returns(ViewEngineResult.Found(viewName, view2));
engine3
.Setup(e => e.FindView(It.IsAny<ActionContext>(), viewName, /*isPartial*/ false))
.Returns(ViewEngineResult.Found(viewName, view3));
var optionsAccessor = new TestOptionsManager<MvcViewOptions>(); var optionsAccessor = new TestOptionsManager<MvcViewOptions>();
optionsAccessor.Value.ViewEngines.Add(engine1.Object); optionsAccessor.Value.ViewEngines.Add(engine1.Object);
@ -115,7 +120,7 @@ namespace Microsoft.AspNet.Mvc.ViewEngines
var compositeViewEngine = new CompositeViewEngine(optionsAccessor); var compositeViewEngine = new CompositeViewEngine(optionsAccessor);
// Act // Act
var result = compositeViewEngine.FindView(GetActionContext(), viewName); var result = compositeViewEngine.FindView(GetActionContext(), viewName, isPartial: false);
// Assert // Assert
Assert.True(result.Success); Assert.True(result.Success);
@ -128,15 +133,18 @@ namespace Microsoft.AspNet.Mvc.ViewEngines
{ {
// Arrange // Arrange
var viewName = "foo"; var viewName = "foo";
var engine1 = new Mock<IViewEngine>(); var engine1 = new Mock<IViewEngine>(MockBehavior.Strict);
var engine2 = new Mock<IViewEngine>(); var engine2 = new Mock<IViewEngine>(MockBehavior.Strict);
var engine3 = new Mock<IViewEngine>(); var engine3 = new Mock<IViewEngine>(MockBehavior.Strict);
engine1.Setup(e => e.FindView(It.IsAny<ActionContext>(), It.IsAny<string>())) engine1
.Returns(ViewEngineResult.NotFound(viewName, new[] { "1", "2" })); .Setup(e => e.FindView(It.IsAny<ActionContext>(), viewName, /*isPartial*/ false))
engine2.Setup(e => e.FindView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Returns(ViewEngineResult.NotFound(viewName, new[] { "1", "2" }));
.Returns(ViewEngineResult.NotFound(viewName, new[] { "3" })); engine2
engine3.Setup(e => e.FindView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(e => e.FindView(It.IsAny<ActionContext>(), viewName, /*isPartial*/ false))
.Returns(ViewEngineResult.NotFound(viewName, new[] { "4", "5" })); .Returns(ViewEngineResult.NotFound(viewName, new[] { "3" }));
engine3
.Setup(e => e.FindView(It.IsAny<ActionContext>(), viewName, /*isPartial*/ false))
.Returns(ViewEngineResult.NotFound(viewName, new[] { "4", "5" }));
var optionsAccessor = new TestOptionsManager<MvcViewOptions>(); var optionsAccessor = new TestOptionsManager<MvcViewOptions>();
optionsAccessor.Value.ViewEngines.Add(engine1.Object); optionsAccessor.Value.ViewEngines.Add(engine1.Object);
@ -145,7 +153,149 @@ namespace Microsoft.AspNet.Mvc.ViewEngines
var compositeViewEngine = new CompositeViewEngine(optionsAccessor); var compositeViewEngine = new CompositeViewEngine(optionsAccessor);
// Act // Act
var result = compositeViewEngine.FindView(GetActionContext(), viewName); var result = compositeViewEngine.FindView(GetActionContext(), viewName, isPartial: false);
// Assert
Assert.False(result.Success);
Assert.Equal(new[] { "1", "2", "3", "4", "5" }, result.SearchedLocations);
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public void GetView_ReturnsNotFoundResult_WhenNoViewEnginesAreRegistered(bool isPartial)
{
// Arrange
var viewName = "test-view.cshtml";
var optionsAccessor = new TestOptionsManager<MvcViewOptions>();
var compositeViewEngine = new CompositeViewEngine(optionsAccessor);
// Act
var result = compositeViewEngine.GetView("~/Index.html", viewName, isPartial);
// Assert
Assert.False(result.Success);
Assert.Empty(result.SearchedLocations);
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public void GetView_ReturnsNotFoundResult_WhenExactlyOneViewEngineIsRegisteredWhichReturnsNotFoundResult(
bool isPartial)
{
// Arrange
var viewName = "test-view.cshtml";
var expectedViewName = "~/" + viewName;
var engine = new Mock<IViewEngine>(MockBehavior.Strict);
engine
.Setup(e => e.GetView("~/Index.html", viewName, isPartial))
.Returns(ViewEngineResult.NotFound(expectedViewName, new[] { expectedViewName }));
var optionsAccessor = new TestOptionsManager<MvcViewOptions>();
optionsAccessor.Value.ViewEngines.Add(engine.Object);
var compositeViewEngine = new CompositeViewEngine(optionsAccessor);
// Act
var result = compositeViewEngine.GetView("~/Index.html", viewName, isPartial);
// Assert
Assert.False(result.Success);
Assert.Equal(new[] { expectedViewName }, result.SearchedLocations);
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public void GetView_ReturnsView_WhenExactlyOneViewEngineIsRegisteredWhichReturnsAFoundResult(bool isPartial)
{
// Arrange
var viewName = "test-view.cshtml";
var expectedViewName = "~/" + viewName;
var engine = new Mock<IViewEngine>(MockBehavior.Strict);
var view = Mock.Of<IView>();
engine
.Setup(e => e.GetView("~/Index.html", viewName, isPartial))
.Returns(ViewEngineResult.Found(expectedViewName, view));
var optionsAccessor = new TestOptionsManager<MvcViewOptions>();
optionsAccessor.Value.ViewEngines.Add(engine.Object);
var compositeViewEngine = new CompositeViewEngine(optionsAccessor);
// Act
var result = compositeViewEngine.GetView("~/Index.html", viewName, isPartial);
// Assert
Assert.True(result.Success);
Assert.Same(view, result.View);
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public void GetView_ReturnsViewFromFirstViewEngineWithFoundResult(bool isPartial)
{
// Arrange
var viewName = "foo.cshtml";
var expectedViewName = "~/" + viewName;
var engine1 = new Mock<IViewEngine>(MockBehavior.Strict);
var engine2 = new Mock<IViewEngine>(MockBehavior.Strict);
var engine3 = new Mock<IViewEngine>(MockBehavior.Strict);
var view2 = Mock.Of<IView>();
var view3 = Mock.Of<IView>();
engine1
.Setup(e => e.GetView("~/Index.html", viewName, isPartial))
.Returns(ViewEngineResult.NotFound(expectedViewName, Enumerable.Empty<string>()));
engine2
.Setup(e => e.GetView("~/Index.html", viewName, isPartial))
.Returns(ViewEngineResult.Found(expectedViewName, view2));
engine3
.Setup(e => e.GetView("~/Index.html", viewName, isPartial))
.Returns(ViewEngineResult.Found(expectedViewName, view3));
var optionsAccessor = new TestOptionsManager<MvcViewOptions>();
optionsAccessor.Value.ViewEngines.Add(engine1.Object);
optionsAccessor.Value.ViewEngines.Add(engine2.Object);
optionsAccessor.Value.ViewEngines.Add(engine3.Object);
var compositeViewEngine = new CompositeViewEngine(optionsAccessor);
// Act
var result = compositeViewEngine.GetView("~/Index.html", viewName, isPartial);
// Assert
Assert.True(result.Success);
Assert.Same(view2, result.View);
Assert.Equal(expectedViewName, result.ViewName);
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public void GetView_ReturnsNotFound_IfAllViewEnginesReturnNotFound(bool isPartial)
{
// Arrange
var viewName = "foo.cshtml";
var expectedViewName = "~/" + viewName;
var engine1 = new Mock<IViewEngine>(MockBehavior.Strict);
var engine2 = new Mock<IViewEngine>(MockBehavior.Strict);
var engine3 = new Mock<IViewEngine>(MockBehavior.Strict);
engine1
.Setup(e => e.GetView("~/Index.html", viewName, isPartial))
.Returns(ViewEngineResult.NotFound(expectedViewName, new[] { "1", "2" }));
engine2
.Setup(e => e.GetView("~/Index.html", viewName, isPartial))
.Returns(ViewEngineResult.NotFound(expectedViewName, new[] { "3" }));
engine3
.Setup(e => e.GetView("~/Index.html", viewName, isPartial))
.Returns(ViewEngineResult.NotFound(expectedViewName, new[] { "4", "5" }));
var optionsAccessor = new TestOptionsManager<MvcViewOptions>();
optionsAccessor.Value.ViewEngines.Add(engine1.Object);
optionsAccessor.Value.ViewEngines.Add(engine2.Object);
optionsAccessor.Value.ViewEngines.Add(engine3.Object);
var compositeViewEngine = new CompositeViewEngine(optionsAccessor);
// Act
var result = compositeViewEngine.GetView("~/Index.html", viewName, isPartial);
// Assert // Assert
Assert.False(result.Success); Assert.False(result.Success);
@ -153,7 +303,7 @@ namespace Microsoft.AspNet.Mvc.ViewEngines
} }
[Fact] [Fact]
public void FindPartialView_ReturnsNotFoundResult_WhenNoViewEnginesAreRegistered() public void FindView_IsPartial_ReturnsNotFoundResult_WhenNoViewEnginesAreRegistered()
{ {
// Arrange // Arrange
var viewName = "my-partial-view"; var viewName = "my-partial-view";
@ -161,7 +311,7 @@ namespace Microsoft.AspNet.Mvc.ViewEngines
var compositeViewEngine = new CompositeViewEngine(optionsAccessor); var compositeViewEngine = new CompositeViewEngine(optionsAccessor);
// Act // Act
var result = compositeViewEngine.FindPartialView(GetActionContext(), viewName); var result = compositeViewEngine.FindView(GetActionContext(), viewName, isPartial: true);
// Assert // Assert
Assert.False(result.Success); Assert.False(result.Success);
@ -169,19 +319,20 @@ namespace Microsoft.AspNet.Mvc.ViewEngines
} }
[Fact] [Fact]
public void FindPartialView_ReturnsNotFoundResult_WhenExactlyOneViewEngineIsRegisteredWhichReturnsNotFoundResult() public void FindView_IsPartial_ReturnsNotFoundResult_WhenExactlyOneViewEngineIsRegisteredWhichReturnsNotFoundResult()
{ {
// Arrange // Arrange
var viewName = "partial-view"; var viewName = "partial-view";
var engine = new Mock<IViewEngine>(); var engine = new Mock<IViewEngine>(MockBehavior.Strict);
engine.Setup(e => e.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) engine
.Returns(ViewEngineResult.NotFound(viewName, new[] { "Shared/partial-view" })); .Setup(e => e.FindView(It.IsAny<ActionContext>(), viewName, /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound(viewName, new[] { "Shared/partial-view" }));
var optionsAccessor = new TestOptionsManager<MvcViewOptions>(); var optionsAccessor = new TestOptionsManager<MvcViewOptions>();
optionsAccessor.Value.ViewEngines.Add(engine.Object); optionsAccessor.Value.ViewEngines.Add(engine.Object);
var compositeViewEngine = new CompositeViewEngine(optionsAccessor); var compositeViewEngine = new CompositeViewEngine(optionsAccessor);
// Act // Act
var result = compositeViewEngine.FindPartialView(GetActionContext(), viewName); var result = compositeViewEngine.FindView(GetActionContext(), viewName, isPartial: true);
// Assert // Assert
Assert.False(result.Success); Assert.False(result.Success);
@ -189,20 +340,21 @@ namespace Microsoft.AspNet.Mvc.ViewEngines
} }
[Fact] [Fact]
public void FindPartialView_ReturnsView_WhenExactlyOneViewEngineIsRegisteredWhichReturnsAFoundResult() public void FindView_IsPartial_ReturnsView_WhenExactlyOneViewEngineIsRegisteredWhichReturnsAFoundResult()
{ {
// Arrange // Arrange
var viewName = "test-view"; var viewName = "test-view";
var engine = new Mock<IViewEngine>(); var engine = new Mock<IViewEngine>(MockBehavior.Strict);
var view = Mock.Of<IView>(); var view = Mock.Of<IView>();
engine.Setup(e => e.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) engine
.Returns(ViewEngineResult.Found(viewName, view)); .Setup(e => e.FindView(It.IsAny<ActionContext>(), viewName, /*isPartial*/ true))
.Returns(ViewEngineResult.Found(viewName, view));
var optionsAccessor = new TestOptionsManager<MvcViewOptions>(); var optionsAccessor = new TestOptionsManager<MvcViewOptions>();
optionsAccessor.Value.ViewEngines.Add(engine.Object); optionsAccessor.Value.ViewEngines.Add(engine.Object);
var compositeViewEngine = new CompositeViewEngine(optionsAccessor); var compositeViewEngine = new CompositeViewEngine(optionsAccessor);
// Act // Act
var result = compositeViewEngine.FindPartialView(GetActionContext(), viewName); var result = compositeViewEngine.FindView(GetActionContext(), viewName, isPartial: true);
// Assert // Assert
Assert.True(result.Success); Assert.True(result.Success);
@ -210,21 +362,24 @@ namespace Microsoft.AspNet.Mvc.ViewEngines
} }
[Fact] [Fact]
public void FindPartialView_ReturnsViewFromFirstViewEngineWithFoundResult() public void FindView_IsPartial_ReturnsViewFromFirstViewEngineWithFoundResult()
{ {
// Arrange // Arrange
var viewName = "bar"; var viewName = "bar";
var engine1 = new Mock<IViewEngine>(); var engine1 = new Mock<IViewEngine>(MockBehavior.Strict);
var engine2 = new Mock<IViewEngine>(); var engine2 = new Mock<IViewEngine>(MockBehavior.Strict);
var engine3 = new Mock<IViewEngine>(); var engine3 = new Mock<IViewEngine>(MockBehavior.Strict);
var view2 = Mock.Of<IView>(); var view2 = Mock.Of<IView>();
var view3 = Mock.Of<IView>(); var view3 = Mock.Of<IView>();
engine1.Setup(e => e.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) engine1
.Returns(ViewEngineResult.NotFound(viewName, Enumerable.Empty<string>())); .Setup(e => e.FindView(It.IsAny<ActionContext>(), viewName, /*isPartial*/ true))
engine2.Setup(e => e.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Returns(ViewEngineResult.NotFound(viewName, Enumerable.Empty<string>()));
.Returns(ViewEngineResult.Found(viewName, view2)); engine2
engine3.Setup(e => e.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(e => e.FindView(It.IsAny<ActionContext>(), viewName, /*isPartial*/ true))
.Returns(ViewEngineResult.Found(viewName, view3)); .Returns(ViewEngineResult.Found(viewName, view2));
engine3
.Setup(e => e.FindView(It.IsAny<ActionContext>(), viewName, /*isPartial*/ true))
.Returns(ViewEngineResult.Found(viewName, view3));
var optionsAccessor = new TestOptionsManager<MvcViewOptions>(); var optionsAccessor = new TestOptionsManager<MvcViewOptions>();
optionsAccessor.Value.ViewEngines.Add(engine1.Object); optionsAccessor.Value.ViewEngines.Add(engine1.Object);
@ -233,7 +388,7 @@ namespace Microsoft.AspNet.Mvc.ViewEngines
var compositeViewEngine = new CompositeViewEngine(optionsAccessor); var compositeViewEngine = new CompositeViewEngine(optionsAccessor);
// Act // Act
var result = compositeViewEngine.FindPartialView(GetActionContext(), viewName); var result = compositeViewEngine.FindView(GetActionContext(), viewName, isPartial: true);
// Assert // Assert
Assert.True(result.Success); Assert.True(result.Success);
@ -242,19 +397,22 @@ namespace Microsoft.AspNet.Mvc.ViewEngines
} }
[Fact] [Fact]
public void FindPartialView_ReturnsNotFound_IfAllViewEnginesReturnNotFound() public void FindView_IsPartial_ReturnsNotFound_IfAllViewEnginesReturnNotFound()
{ {
// Arrange // Arrange
var viewName = "foo"; var viewName = "foo";
var engine1 = new Mock<IViewEngine>(); var engine1 = new Mock<IViewEngine>(MockBehavior.Strict);
var engine2 = new Mock<IViewEngine>(); var engine2 = new Mock<IViewEngine>(MockBehavior.Strict);
var engine3 = new Mock<IViewEngine>(); var engine3 = new Mock<IViewEngine>(MockBehavior.Strict);
engine1.Setup(e => e.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) engine1
.Returns(ViewEngineResult.NotFound(viewName, new[] { "1", "2" })); .Setup(e => e.FindView(It.IsAny<ActionContext>(), viewName, /*isPartial*/ true))
engine2.Setup(e => e.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Returns(ViewEngineResult.NotFound(viewName, new[] { "1", "2" }));
.Returns(ViewEngineResult.NotFound(viewName, new[] { "3" })); engine2
engine3.Setup(e => e.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(e => e.FindView(It.IsAny<ActionContext>(), viewName, /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound(viewName, new[] { "4", "5" })); .Returns(ViewEngineResult.NotFound(viewName, new[] { "3" }));
engine3
.Setup(e => e.FindView(It.IsAny<ActionContext>(), viewName, /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound(viewName, new[] { "4", "5" }));
var optionsAccessor = new TestOptionsManager<MvcViewOptions>(); var optionsAccessor = new TestOptionsManager<MvcViewOptions>();
optionsAccessor.Value.ViewEngines.Add(engine1.Object); optionsAccessor.Value.ViewEngines.Add(engine1.Object);
@ -263,7 +421,7 @@ namespace Microsoft.AspNet.Mvc.ViewEngines
var compositeViewEngine = new CompositeViewEngine(optionsAccessor); var compositeViewEngine = new CompositeViewEngine(optionsAccessor);
// Act // Act
var result = compositeViewEngine.FindPartialView(GetActionContext(), viewName); var result = compositeViewEngine.FindView(GetActionContext(), viewName, isPartial: true);
// Assert // Assert
Assert.False(result.Success); Assert.False(result.Success);
@ -285,12 +443,12 @@ namespace Microsoft.AspNet.Mvc.ViewEngines
public ITestService Service { get; private set; } public ITestService Service { get; private set; }
public ViewEngineResult FindPartialView(ActionContext context, string partialViewName) public ViewEngineResult FindView(ActionContext context, string viewName, bool isPartial)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public ViewEngineResult FindView(ActionContext context, string viewName) public ViewEngineResult GetView(string executingFilePath, string viewPath, bool isPartial)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

View File

@ -128,9 +128,13 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
"<div class=\"HtmlEncode[[display-field]]\"></div>"+ Environment.NewLine; "<div class=\"HtmlEncode[[display-field]]\"></div>"+ Environment.NewLine;
var model = new DefaultTemplatesUtilities.ObjectWithScaffoldColumn(); var model = new DefaultTemplatesUtilities.ObjectWithScaffoldColumn();
var viewEngine = new Mock<ICompositeViewEngine>(); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) viewEngine
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>())); .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
var htmlHelper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); var htmlHelper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
// Act // Act
@ -259,10 +263,13 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
{ {
// Arrange // Arrange
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "Model string" }; var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "Model string" };
var viewEngine = new Mock<ICompositeViewEngine>(); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>())); .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
helper.ViewData["Property1"] = "ViewData string"; helper.ViewData["Property1"] = "ViewData string";
@ -278,10 +285,13 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
{ {
// Arrange // Arrange
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "Model string" }; var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "Model string" };
var viewEngine = new Mock<ICompositeViewEngine>(); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>())); .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
helper.ViewData["Property1"] = "ViewData string"; helper.ViewData["Property1"] = "ViewData string";
@ -297,10 +307,13 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
{ {
// Arrange // Arrange
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "Model string" }; var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "Model string" };
var viewEngine = new Mock<ICompositeViewEngine>(); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>())); .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
// Act // Act
@ -317,10 +330,13 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
{ {
// Arrange // Arrange
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = propertyValue, }; var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = propertyValue, };
var viewEngine = new Mock<ICompositeViewEngine>(); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>())); .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
helper.ViewData["Property1"] = "ViewData string"; helper.ViewData["Property1"] = "ViewData string";
@ -343,9 +359,12 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
{ {
throw new ArgumentException(expectedMessage); throw new ArgumentException(expectedMessage);
})); }));
var viewEngine = new Mock<ICompositeViewEngine>(); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.Found("test-view", view.Object)); .Returns(ViewEngineResult.Found("test-view", view.Object));
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
helper.ViewData["Property1"] = "ViewData string"; helper.ViewData["Property1"] = "ViewData string";
@ -356,14 +375,15 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
} }
[Fact] [Fact]
public void Display_CallsFindPartialView_WithExpectedPath() public void Display_CallsFindView_WithIsPartialAndExpectedPath()
{ {
// Arrange // Arrange
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isPartial*/ true))
It.Is<string>(view => view.Equals("DisplayTemplates/String")))) .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), "DisplayTemplates/String", /*isPartial*/ true))
.Returns(ViewEngineResult.Found(string.Empty, new Mock<IView>().Object)) .Returns(ViewEngineResult.Found(string.Empty, new Mock<IView>().Object))
.Verifiable(); .Verifiable();
var html = DefaultTemplatesUtilities.GetHtmlHelper(new object(), viewEngine: viewEngine.Object); var html = DefaultTemplatesUtilities.GetHtmlHelper(new object(), viewEngine: viewEngine.Object);

View File

@ -189,9 +189,13 @@ Environment.NewLine +
Environment.NewLine; Environment.NewLine;
var model = new DefaultTemplatesUtilities.ObjectWithScaffoldColumn(); var model = new DefaultTemplatesUtilities.ObjectWithScaffoldColumn();
var viewEngine = new Mock<ICompositeViewEngine>(); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) viewEngine
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>())); .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>()));
var htmlHelper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); var htmlHelper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
// Act // Act
@ -354,10 +358,13 @@ Environment.NewLine;
{ {
// Arrange // Arrange
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "True" }; var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "True" };
var viewEngine = new Mock<ICompositeViewEngine>(); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>())); .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
var helper = DefaultTemplatesUtilities.GetHtmlHelper( var helper = DefaultTemplatesUtilities.GetHtmlHelper(
model, model,
viewEngine.Object, viewEngine.Object,
@ -384,10 +391,13 @@ Environment.NewLine;
{ {
// Arrange // Arrange
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "True" }; var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "True" };
var viewEngine = new Mock<ICompositeViewEngine>(); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>())); .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
var helper = DefaultTemplatesUtilities.GetHtmlHelper( var helper = DefaultTemplatesUtilities.GetHtmlHelper(
model, model,
viewEngine.Object, viewEngine.Object,
@ -413,10 +423,13 @@ Environment.NewLine;
{ {
// Arrange // Arrange
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "True" }; var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "True" };
var viewEngine = new Mock<ICompositeViewEngine>(); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>())); .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
var provider = new TestModelMetadataProvider(); var provider = new TestModelMetadataProvider();
provider.ForProperty<DefaultTemplatesUtilities.ObjectTemplateModel>("Property1").DisplayDetails(dd => provider.ForProperty<DefaultTemplatesUtilities.ObjectTemplateModel>("Property1").DisplayDetails(dd =>
@ -452,10 +465,13 @@ Environment.NewLine;
{ {
// Arrange // Arrange
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "True" }; var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "True" };
var viewEngine = new Mock<ICompositeViewEngine>(); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>())); .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
var provider = new TestModelMetadataProvider(); var provider = new TestModelMetadataProvider();
provider.ForProperty<DefaultTemplatesUtilities.ObjectTemplateModel>("Property1").DisplayDetails(dd => provider.ForProperty<DefaultTemplatesUtilities.ObjectTemplateModel>("Property1").DisplayDetails(dd =>
@ -490,10 +506,13 @@ Environment.NewLine;
{ {
// Arrange // Arrange
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "True" }; var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "True" };
var viewEngine = new Mock<ICompositeViewEngine>(); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>())); .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
var provider = new TestModelMetadataProvider(); var provider = new TestModelMetadataProvider();
provider.ForProperty<DefaultTemplatesUtilities.ObjectTemplateModel>("Property1").DisplayDetails(dd => provider.ForProperty<DefaultTemplatesUtilities.ObjectTemplateModel>("Property1").DisplayDetails(dd =>
@ -529,10 +548,13 @@ Environment.NewLine;
{ {
// Arrange // Arrange
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "True" }; var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "True" };
var viewEngine = new Mock<ICompositeViewEngine>(); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>())); .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
var provider = new TestModelMetadataProvider(); var provider = new TestModelMetadataProvider();
provider.ForProperty<DefaultTemplatesUtilities.ObjectTemplateModel>("Property1").DisplayDetails(dd => provider.ForProperty<DefaultTemplatesUtilities.ObjectTemplateModel>("Property1").DisplayDetails(dd =>
@ -566,10 +588,13 @@ Environment.NewLine;
{ {
// Arrange // Arrange
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "Model string" }; var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "Model string" };
var viewEngine = new Mock<ICompositeViewEngine>(); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>())); .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
helper.ViewData["Property1"] = "ViewData string"; helper.ViewData["Property1"] = "ViewData string";
@ -597,7 +622,7 @@ Environment.NewLine;
"<input class=\"HtmlEncode[[text-box single-line]]\" data-val=\"HtmlEncode[[true]]\" " + "<input class=\"HtmlEncode[[text-box single-line]]\" data-val=\"HtmlEncode[[true]]\" " +
"data-val-required=\"HtmlEncode[[The DateTimeOffset field is required.]]\" id=\"HtmlEncode[[FieldPrefix]]\" " + "data-val-required=\"HtmlEncode[[The DateTimeOffset field is required.]]\" id=\"HtmlEncode[[FieldPrefix]]\" " +
"name=\"HtmlEncode[[FieldPrefix]]\" type=\"HtmlEncode[[" + "name=\"HtmlEncode[[FieldPrefix]]\" type=\"HtmlEncode[[" +
dataTypeName + dataTypeName +
"]]\" value=\"HtmlEncode[[" + expected + "]]\" />"); "]]\" value=\"HtmlEncode[[" + expected + "]]\" />");
var offset = TimeSpan.FromHours(0); var offset = TimeSpan.FromHours(0);
@ -610,10 +635,13 @@ Environment.NewLine;
second: 5, second: 5,
millisecond: 6, millisecond: 6,
offset: offset); offset: offset);
var viewEngine = new Mock<ICompositeViewEngine>(); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>())); .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
var provider = new TestModelMetadataProvider(); var provider = new TestModelMetadataProvider();
provider.ForType<DateTimeOffset>().DisplayDetails(dd => provider.ForType<DateTimeOffset>().DisplayDetails(dd =>
@ -630,7 +658,7 @@ Environment.NewLine;
helper.ViewData.TemplateInfo.HtmlFieldPrefix = "FieldPrefix"; helper.ViewData.TemplateInfo.HtmlFieldPrefix = "FieldPrefix";
// Act // Act
var result = helper.Editor(""); var result = helper.Editor(string.Empty);
// Assert // Assert
Assert.Equal(expectedInput, HtmlContentUtilities.HtmlContentToString(result)); Assert.Equal(expectedInput, HtmlContentUtilities.HtmlContentToString(result));
@ -650,11 +678,11 @@ Environment.NewLine;
"<input class=\"HtmlEncode[[text-box single-line]]\" data-val=\"HtmlEncode[[true]]\" " + "<input class=\"HtmlEncode[[text-box single-line]]\" data-val=\"HtmlEncode[[true]]\" " +
"data-val-required=\"HtmlEncode[[The DateTimeOffset field is required.]]\" id=\"HtmlEncode[[FieldPrefix]]\" " + "data-val-required=\"HtmlEncode[[The DateTimeOffset field is required.]]\" id=\"HtmlEncode[[FieldPrefix]]\" " +
"name=\"HtmlEncode[[FieldPrefix]]\" type=\"HtmlEncode[[" + "name=\"HtmlEncode[[FieldPrefix]]\" type=\"HtmlEncode[[" +
dataTypeName + dataTypeName +
"]]\" value=\"HtmlEncode[[" + expected + "]]\" />"); "]]\" value=\"HtmlEncode[[" + expected + "]]\" />");
// Place DateTime-local value in current timezone. // Place DateTime-local value in current timezone.
var offset = string.Equals("", dataTypeName) ? DateTimeOffset.Now.Offset : TimeSpan.FromHours(0); var offset = string.Equals(string.Empty, dataTypeName) ? DateTimeOffset.Now.Offset : TimeSpan.FromHours(0);
var model = new DateTimeOffset( var model = new DateTimeOffset(
year: 2000, year: 2000,
month: 1, month: 1,
@ -664,10 +692,13 @@ Environment.NewLine;
second: 5, second: 5,
millisecond: 60, millisecond: 60,
offset: offset); offset: offset);
var viewEngine = new Mock<ICompositeViewEngine>(); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>())); .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
var provider = new TestModelMetadataProvider(); var provider = new TestModelMetadataProvider();
provider.ForType<DateTimeOffset>().DisplayDetails(dd => provider.ForType<DateTimeOffset>().DisplayDetails(dd =>
@ -685,7 +716,7 @@ Environment.NewLine;
helper.ViewData.TemplateInfo.HtmlFieldPrefix = "FieldPrefix"; helper.ViewData.TemplateInfo.HtmlFieldPrefix = "FieldPrefix";
// Act // Act
var result = helper.Editor(""); var result = helper.Editor(string.Empty);
// Assert // Assert
Assert.Equal(expectedInput, HtmlContentUtilities.HtmlContentToString(result)); Assert.Equal(expectedInput, HtmlContentUtilities.HtmlContentToString(result));
@ -708,7 +739,7 @@ Environment.NewLine;
"<input class=\"HtmlEncode[[text-box single-line]]\" data-val=\"HtmlEncode[[true]]\" " + "<input class=\"HtmlEncode[[text-box single-line]]\" data-val=\"HtmlEncode[[true]]\" " +
"data-val-required=\"HtmlEncode[[The DateTimeOffset field is required.]]\" id=\"HtmlEncode[[FieldPrefix]]\" " + "data-val-required=\"HtmlEncode[[The DateTimeOffset field is required.]]\" id=\"HtmlEncode[[FieldPrefix]]\" " +
"name=\"HtmlEncode[[FieldPrefix]]\" type=\"HtmlEncode[[" + "name=\"HtmlEncode[[FieldPrefix]]\" type=\"HtmlEncode[[" +
dataTypeName + dataTypeName +
"]]\" value=\"HtmlEncode[[Formatted as 2000-01-02T03:04:05.0600000+00:00]]\" />"); "]]\" value=\"HtmlEncode[[Formatted as 2000-01-02T03:04:05.0600000+00:00]]\" />");
var offset = TimeSpan.FromHours(0); var offset = TimeSpan.FromHours(0);
@ -721,11 +752,13 @@ Environment.NewLine;
second: 5, second: 5,
millisecond: 60, millisecond: 60,
offset: offset); offset: offset);
var viewEngine = new Mock<ICompositeViewEngine>(); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>())); .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
var provider = new TestModelMetadataProvider(); var provider = new TestModelMetadataProvider();
provider.ForType<DateTimeOffset>().DisplayDetails(dd => provider.ForType<DateTimeOffset>().DisplayDetails(dd =>
@ -745,7 +778,7 @@ Environment.NewLine;
helper.ViewData.TemplateInfo.HtmlFieldPrefix = "FieldPrefix"; helper.ViewData.TemplateInfo.HtmlFieldPrefix = "FieldPrefix";
// Act // Act
var result = helper.Editor(""); var result = helper.Editor(string.Empty);
// Assert // Assert
Assert.Equal(expectedInput, HtmlContentUtilities.HtmlContentToString(result)); Assert.Equal(expectedInput, HtmlContentUtilities.HtmlContentToString(result));
@ -756,10 +789,13 @@ Environment.NewLine;
{ {
// Arrange // Arrange
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "Model string" }; var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "Model string" };
var viewEngine = new Mock<ICompositeViewEngine>(); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>())); .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
helper.ViewData["Property1"] = "ViewData string"; helper.ViewData["Property1"] = "ViewData string";
@ -777,10 +813,13 @@ Environment.NewLine;
{ {
// Arrange // Arrange
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "Model string" }; var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "Model string" };
var viewEngine = new Mock<ICompositeViewEngine>(); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>())); .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
// Act // Act
@ -799,10 +838,13 @@ Environment.NewLine;
{ {
// Arrange // Arrange
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = propertyValue, }; var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = propertyValue, };
var viewEngine = new Mock<ICompositeViewEngine>(); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>())); .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
helper.ViewData["Property1"] = "ViewData string"; helper.ViewData["Property1"] = "ViewData string";
@ -827,9 +869,12 @@ Environment.NewLine;
{ {
throw new FormatException(expectedMessage); throw new FormatException(expectedMessage);
})); }));
var viewEngine = new Mock<ICompositeViewEngine>(); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isPartial*/ true))
.Returns(ViewEngineResult.Found("test-view", view.Object)); .Returns(ViewEngineResult.Found("test-view", view.Object));
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
helper.ViewData["Property1"] = "ViewData string"; helper.ViewData["Property1"] = "ViewData string";
@ -840,14 +885,15 @@ Environment.NewLine;
} }
[Fact] [Fact]
public void EditorForModel_CallsFindPartialView_WithExpectedPath() public void EditorForModel_CallsFindView_WithIsPartialAndExpectedPath()
{ {
// Arrange // Arrange
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isPartial*/ true))
It.Is<string>(view => String.Equals(view, .Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
"EditorTemplates/String")))) viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), "EditorTemplates/String", /*isPartial*/ true))
.Returns(ViewEngineResult.Found(string.Empty, new Mock<IView>().Object)) .Returns(ViewEngineResult.Found(string.Empty, new Mock<IView>().Object))
.Verifiable(); .Verifiable();
var html = DefaultTemplatesUtilities.GetHtmlHelper(new object(), viewEngine: viewEngine.Object); var html = DefaultTemplatesUtilities.GetHtmlHelper(new object(), viewEngine: viewEngine.Object);

View File

@ -3,6 +3,7 @@
#if MOCK_SUPPORT #if MOCK_SUPPORT
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Http.Internal; using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Abstractions; using Microsoft.AspNet.Mvc.Abstractions;
@ -26,9 +27,13 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
var executor = GetViewExecutor(); var executor = GetViewExecutor();
var viewName = "my-view"; var viewName = "my-view";
var viewEngine = new Mock<ICompositeViewEngine>(); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(e => e.FindPartialView(context, viewName)) .Setup(e => e.GetView(/*executingFilePath*/ null, viewName, /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound(viewName, Enumerable.Empty<string>()))
.Verifiable();
viewEngine
.Setup(e => e.FindView(context, viewName, /*isPartial*/ true))
.Returns(ViewEngineResult.Found(viewName, Mock.Of<IView>())) .Returns(ViewEngineResult.Found(viewName, Mock.Of<IView>()))
.Verifiable(); .Verifiable();
@ -118,7 +123,11 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
var viewName = "myview"; var viewName = "myview";
var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict); var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(e => e.FindPartialView(context, "myview")) .Setup(e => e.GetView(/*executingFilePath*/ null, "myview", /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound("myview", Enumerable.Empty<string>()))
.Verifiable();
viewEngine
.Setup(e => e.FindView(context, "myview", /*isPartial*/ true))
.Returns(ViewEngineResult.NotFound("myview", new string[] { "location/myview" })); .Returns(ViewEngineResult.NotFound("myview", new string[] { "location/myview" }));
var viewResult = new PartialViewResult var viewResult = new PartialViewResult
@ -209,8 +218,13 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict); var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(e => e.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(e => e.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isPartial*/ true))
.Returns<ActionContext, string>((_, name) => ViewEngineResult.Found(name, Mock.Of<IView>())); .Returns<string, string, bool>(
(executing, name, isPartial) => ViewEngineResult.NotFound(name, Enumerable.Empty<string>()));
viewEngine
.Setup(e => e.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isPartial*/ true))
.Returns<ActionContext, string, bool>(
(context, name, isPartial) => ViewEngineResult.Found(name, Mock.Of<IView>()));
var options = new TestOptionsManager<MvcViewOptions>(); var options = new TestOptionsManager<MvcViewOptions>();
options.Value.ViewEngines.Add(viewEngine.Object); options.Value.ViewEngines.Add(viewEngine.Object);

View File

@ -3,6 +3,7 @@
#if MOCK_SUPPORT #if MOCK_SUPPORT
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Http.Internal; using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Abstractions; using Microsoft.AspNet.Mvc.Abstractions;
@ -26,9 +27,13 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
var executor = GetViewExecutor(); var executor = GetViewExecutor();
var viewName = "my-view"; var viewName = "my-view";
var viewEngine = new Mock<ICompositeViewEngine>(); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(e => e.FindView(context, viewName)) .Setup(e => e.GetView(/*executingFilePath*/ null, viewName, /*isPartial*/ false))
.Returns(ViewEngineResult.NotFound(viewName, Enumerable.Empty<string>()))
.Verifiable();
viewEngine
.Setup(e => e.FindView(context, viewName, /*isPartial*/ false))
.Returns(ViewEngineResult.Found(viewName, Mock.Of<IView>())) .Returns(ViewEngineResult.Found(viewName, Mock.Of<IView>()))
.Verifiable(); .Verifiable();
@ -116,9 +121,12 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
var executor = GetViewExecutor(diagnosticSource); var executor = GetViewExecutor(diagnosticSource);
var viewName = "myview"; var viewName = "myview";
var viewEngine = new Mock<IViewEngine>(); var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(e => e.FindView(context, "myview")) .Setup(e => e.GetView(/*executingFilePath*/ null, "myview", /*isPartial*/ false))
.Returns(ViewEngineResult.NotFound("myview", Enumerable.Empty<string>()));
viewEngine
.Setup(e => e.FindView(context, "myview", /*isPartial*/ false))
.Returns(ViewEngineResult.NotFound("myview", new string[] { "location/myview" })); .Returns(ViewEngineResult.NotFound("myview", new string[] { "location/myview" }));
var viewResult = new ViewResult var viewResult = new ViewResult
@ -209,8 +217,13 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict); var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(e => e.FindView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(e => e.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isPartial*/ false))
.Returns<ActionContext, string>((_, name) => ViewEngineResult.Found(name, Mock.Of<IView>())); .Returns<string, string, bool>(
(path, name, partial) => ViewEngineResult.NotFound(name, Enumerable.Empty<string>()));
viewEngine
.Setup(e => e.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isPartial*/ false))
.Returns<ActionContext, string, bool>(
(context, name, partial) => ViewEngineResult.Found(name, Mock.Of<IView>()));
var options = new TestOptionsManager<MvcViewOptions>(); var options = new TestOptionsManager<MvcViewOptions>();
options.Value.ViewEngines.Add(viewEngine.Object); options.Value.ViewEngines.Add(viewEngine.Object);

View File

@ -4,6 +4,7 @@
#if MOCK_SUPPORT #if MOCK_SUPPORT
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Http; using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Internal; using Microsoft.AspNet.Http.Internal;
@ -36,9 +37,13 @@ namespace Microsoft.AspNet.Mvc
var actionContext = GetActionContext(); var actionContext = GetActionContext();
var viewEngine = new Mock<IViewEngine>(); var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>())) .Setup(e => e.GetView(/*executingFilePath*/ null, "MyView", /*isPartial*/ false))
.Returns(ViewEngineResult.NotFound("MyView", Enumerable.Empty<string>()))
.Verifiable();
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isPartial*/ false))
.Returns(ViewEngineResult.NotFound("MyView", new[] { "Location1", "Location2" })) .Returns(ViewEngineResult.NotFound("MyView", new[] { "Location1", "Location2" }))
.Verifiable(); .Verifiable();
@ -81,7 +86,11 @@ namespace Microsoft.AspNet.Mvc
var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict); var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(e => e.FindView(context, "myview")) .Setup(e => e.GetView(/*executingFilePath*/ null, "myview", /*isPartial*/ false))
.Returns(ViewEngineResult.NotFound("myview", Enumerable.Empty<string>()))
.Verifiable();
viewEngine
.Setup(e => e.FindView(context, "myview", /*isPartial*/ false))
.Returns(ViewEngineResult.Found("myview", view.Object)) .Returns(ViewEngineResult.Found("myview", view.Object))
.Verifiable(); .Verifiable();

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Linq;
using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.ViewEngines; using Microsoft.AspNet.Mvc.ViewEngines;
@ -9,22 +10,22 @@ namespace CompositeViewEngineWebSite
{ {
public class TestViewEngine : IViewEngine public class TestViewEngine : IViewEngine
{ {
public ViewEngineResult FindPartialView(ActionContext context, string partialViewName) public ViewEngineResult FindView(ActionContext context, string viewName, bool isPartial)
{ {
if (string.Equals(partialViewName, "partial-test-view", StringComparison.Ordinal)) if (string.Equals(viewName, "partial-test-view", StringComparison.Ordinal) ||
string.Equals(viewName, "test-view", StringComparison.Ordinal))
{ {
return ViewEngineResult.Found(partialViewName, new TestPartialView()); var view = isPartial ? (IView)new TestPartialView() : new TestView();
return ViewEngineResult.Found(viewName, view);
} }
return ViewEngineResult.NotFound(partialViewName, new[] { partialViewName });
return ViewEngineResult.NotFound(viewName, Enumerable.Empty<string>());
} }
public ViewEngineResult FindView(ActionContext context, string viewName) public ViewEngineResult GetView(string executingFilePath, string viewPath, bool isPartial)
{ {
if (string.Equals(viewName, "test-view")) return ViewEngineResult.NotFound(viewPath, Enumerable.Empty<string>());
{
return ViewEngineResult.Found(viewName, new TestView());
}
return ViewEngineResult.NotFound(viewName, new[] { viewName });
} }
} }
} }

View File

@ -22,7 +22,7 @@ namespace ErrorPageMiddlewareWebSite
[HttpGet("/ErrorFromViewImports")] [HttpGet("/ErrorFromViewImports")]
public IActionResult ViewImportsError() public IActionResult ViewImportsError()
{ {
return View("~/Views/ErrorFromViewImports/Index"); return View("~/Views/ErrorFromViewImports/Index.cshtml");
} }
} }
} }

View File

@ -14,18 +14,18 @@ namespace PrecompilationWebSite.Controllers
public IActionResult PrecompiledViewsCanConsumeCompilationOptions() public IActionResult PrecompiledViewsCanConsumeCompilationOptions()
{ {
return View("~/Views/ViewsConsumingCompilationOptions/Index"); return View("~/Views/ViewsConsumingCompilationOptions/Index.cshtml");
} }
public IActionResult GlobalDeletedPriorToFirstRequest() public IActionResult GlobalDeletedPriorToFirstRequest()
{ {
return View("~/Views/ViewImportsDelete/Index"); return View("~/Views/ViewImportsDelete/Index.cshtml");
} }
[HttpGet("/Test")] [HttpGet("/Test")]
public IActionResult TestView() public IActionResult TestView()
{ {
return View("~/Views/Test/Index"); return View("~/Views/Test/Index.cshtml");
} }
} }
} }

View File

@ -0,0 +1,15 @@
// 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 Microsoft.AspNet.Mvc;
namespace RazorWebSite.Components
{
public class ComponentWithRelativePath : ViewComponent
{
public IViewComponentResult Invoke(Person person)
{
return View("../Shared/Components/ComponentWithRelativePath.cshtml", person);
}
}
}

View File

@ -10,7 +10,7 @@ namespace RazorWebSite.Components
{ {
public IViewComponentResult Invoke(Address address) public IViewComponentResult Invoke(Address address)
{ {
return View("/Views/InheritingInherits/_ViewComponent", address); return View("/Views/InheritingInherits/_ViewComponent.cshtml", address);
} }
} }
} }

View File

@ -29,7 +29,7 @@ namespace RazorWebSite
} }
}; };
return View("/Views/InheritingInherits/Index", model); return View("/Views/InheritingInherits/Index.cshtml", model);
} }
} }
} }

View File

@ -14,7 +14,7 @@ namespace RazorWebSite.Controllers
public IActionResult ViewWithFullPath() public IActionResult ViewWithFullPath()
{ {
return PartialView(@"/Views/ViewEngine/ViewWithFullPath.cshtml"); return PartialView("/Views/ViewEngine/ViewWithFullPath.rzr");
} }
public IActionResult PartialViewWithNamePassedIn() public IActionResult PartialViewWithNamePassedIn()

View File

@ -15,7 +15,12 @@ namespace RazorWebSite.Controllers
public IActionResult ViewWithFullPath() public IActionResult ViewWithFullPath()
{ {
return View(@"/Views/ViewEngine/ViewWithFullPath.cshtml"); return View("/Views/ViewEngine/ViewWithFullPath.rzr");
}
public IActionResult ViewWithRelativePath()
{
return View("Views/ViewEngine/ViewWithRelativePath.cshtml");
} }
public IActionResult ViewWithLayout() public IActionResult ViewWithLayout()
@ -35,6 +40,7 @@ namespace RazorWebSite.Controllers
{ {
Address = new Address { ZipCode = "98052" } Address = new Address { ZipCode = "98052" }
}; };
return View(model); return View(model);
} }

View File

@ -17,12 +17,12 @@ namespace RazorWebSite.Controllers
return View("LayoutSpecifiedWithPartialPathInViewStart"); return View("LayoutSpecifiedWithPartialPathInViewStart");
} }
public IActionResult LayoutSpecifiedWithPartialPathInViewStart_ForViewSpecifiedWithAppRelativePath() public IActionResult LayoutSpecifiedWithPartialPathInViewStart_ForViewSpecifiedWithRelativePath()
{ {
return View("~/Views/ViewNameSpecification_Home/LayoutSpecifiedWithPartialPathInViewStart"); return View("Views/ViewNameSpecification_Home/LayoutSpecifiedWithPartialPathInViewStart.cshtml");
} }
public IActionResult LayoutSpecifiedWithPartialPathInViewStart_ForViewSpecifiedWithAppRelativePathWithExtension() public IActionResult LayoutSpecifiedWithPartialPathInViewStart_ForViewSpecifiedWithAppRelativePath()
{ {
return View("~/Views/ViewNameSpecification_Home/LayoutSpecifiedWithPartialPathInViewStart.cshtml"); return View("~/Views/ViewNameSpecification_Home/LayoutSpecifiedWithPartialPathInViewStart.cshtml");
} }
@ -37,23 +37,23 @@ namespace RazorWebSite.Controllers
return View("LayoutSpecifiedWithPartialPathInPage"); return View("LayoutSpecifiedWithPartialPathInPage");
} }
public IActionResult LayoutSpecifiedWithPartialPathInPageWithAppRelativePath() public IActionResult LayoutSpecifiedWithPartialPathInPageWithRelativePath()
{ {
return View("~/Views/ViewNameSpecification_Home/LayoutSpecifiedWithPartialPathInPage"); return View("Views/ViewNameSpecification_Home/LayoutSpecifiedWithPartialPathInPage.cshtml");
} }
public IActionResult LayoutSpecifiedWithPartialPathInPageWithAppRelativePathWithExtension() public IActionResult LayoutSpecifiedWithPartialPathInPageWithAppRelativePath()
{ {
return View("~/Views/ViewNameSpecification_Home/LayoutSpecifiedWithPartialPathInPage.cshtml"); return View("~/Views/ViewNameSpecification_Home/LayoutSpecifiedWithPartialPathInPage.cshtml");
} }
public IActionResult LayoutSpecifiedWithNonPartialPath() public IActionResult LayoutSpecifiedWithRelativePath()
{ {
ViewData["Layout"] = "~/Views/ViewNameSpecification_Home/_NonSharedLayout"; ViewData["Layout"] = "_NonSharedLayout.cshtml";
return View("PageWithNonPartialLayoutPath"); return View("PageWithNonPartialLayoutPath");
} }
public IActionResult LayoutSpecifiedWithNonPartialPathWithExtension() public IActionResult LayoutSpecifiedWithAppRelativePath()
{ {
ViewData["Layout"] = "~/Views/ViewNameSpecification_Home/_NonSharedLayout.cshtml"; ViewData["Layout"] = "~/Views/ViewNameSpecification_Home/_NonSharedLayout.cshtml";
return View("PageWithNonPartialLayoutPath"); return View("PageWithNonPartialLayoutPath");
@ -65,13 +65,13 @@ namespace RazorWebSite.Controllers
return View("ViewWithPartials"); return View("ViewWithPartials");
} }
public IActionResult ViewWithPartial_SpecifiedWithAbsoluteName() public IActionResult ViewWithPartial_SpecifiedWithRelativePath()
{ {
ViewBag.Partial = "~/Views/ViewNameSpecification_Home/NonSharedPartial"; ViewBag.Partial = "NonSharedPartial.cshtml";
return View("ViewWithPartials"); return View("ViewWithPartials");
} }
public IActionResult ViewWithPartial_SpecifiedWithAbsoluteNameAndExtension() public IActionResult ViewWithPartial_SpecifiedWithAppRelativePath()
{ {
ViewBag.Partial = "~/Views/ViewNameSpecification_Home/NonSharedPartial.cshtml"; ViewBag.Partial = "~/Views/ViewNameSpecification_Home/NonSharedPartial.cshtml";
return View("ViewWithPartials"); return View("ViewWithPartials");

View File

@ -0,0 +1,7 @@
@model Person
@{
Layout = "../_ComponentLayout.cshtml";
}
Component with Relative Path
@Html.DisplayFor(model => model.Name, templateName: "~/Views/Shared/DisplayTemplates/Name.cshtml")
@Html.DisplayFor(model => model.Address, templateName: "../../InheritingInherits/_ViewComponent.cshtml")

View File

@ -0,0 +1,2 @@
@model string
<label><strong>Name:</strong> @Html.DisplayTextFor(model => model)</label>

View File

@ -0,0 +1,15 @@
@{
Layout = "_NestedLayout.cshtml";
ViewData["Title"] = "View with relative path title";
var person = new Person
{
Address = new Address
{
ZipCode = "98052",
},
Name = "Fred",
};
}
ViewWithRelativePath-content
<partial>@await Html.PartialAsync("../Shared/_PartialThatSetsTitle.cshtml")</partial>
@await Component.InvokeAsync("ComponentWithRelativePath", person)