515 lines
17 KiB
C#
515 lines
17 KiB
C#
// Copyright (c) .NET Foundation. All rights reserved.
|
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
|
|
using System.Collections.Generic;
|
|
using System.Net;
|
|
using System.Net.Http;
|
|
using System.Reflection;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.AspNetCore.Localization;
|
|
using Microsoft.AspNetCore.Testing;
|
|
using Microsoft.Net.Http.Headers;
|
|
using Xunit;
|
|
|
|
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|
{
|
|
public class ViewEngineTests : IClassFixture<MvcTestFixture<RazorWebSite.Startup>>
|
|
{
|
|
private static readonly Assembly _assembly = typeof(ViewEngineTests).GetTypeInfo().Assembly;
|
|
|
|
public ViewEngineTests(MvcTestFixture<RazorWebSite.Startup> fixture)
|
|
{
|
|
Client = fixture.CreateDefaultClient();
|
|
}
|
|
|
|
public HttpClient Client { get; }
|
|
|
|
public static IEnumerable<object[]> RazorView_ExecutesPageAndLayoutData
|
|
{
|
|
get
|
|
{
|
|
yield return new[] { "ViewWithoutLayout", @"ViewWithoutLayout-Content" };
|
|
yield return new[]
|
|
{
|
|
"ViewWithLayout",
|
|
@"<layout>
|
|
ViewWithLayout-Content
|
|
</layout>"
|
|
};
|
|
yield return new[]
|
|
{
|
|
"ViewWithFullPath",
|
|
@"<layout>
|
|
ViewWithFullPath-content
|
|
</layout>"
|
|
};
|
|
yield return new[]
|
|
{
|
|
"ViewWithNestedLayout",
|
|
@"<layout>
|
|
<nested-layout>
|
|
/ViewEngine/ViewWithNestedLayout
|
|
ViewWithNestedLayout-Content
|
|
</nested-layout>
|
|
</layout>"
|
|
};
|
|
|
|
yield return new[]
|
|
{
|
|
"ViewWithDataFromController",
|
|
"<h1>hello from controller</h1>"
|
|
};
|
|
}
|
|
}
|
|
|
|
[Theory]
|
|
[MemberData(nameof(RazorView_ExecutesPageAndLayoutData))]
|
|
public async Task RazorView_ExecutesPageAndLayout(string actionName, string expected)
|
|
{
|
|
// Arrange & Act
|
|
var response = await Client.GetAsync("http://localhost/ViewEngine/" + actionName);
|
|
|
|
// Assert
|
|
await response.AssertStatusCodeAsync(HttpStatusCode.OK);
|
|
var body = await response.Content.ReadAsStringAsync();
|
|
|
|
Assert.Equal(expected, body.Trim(), ignoreLineEndingDifferences: true);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task RazorView_ExecutesPartialPagesWithCorrectContext()
|
|
{
|
|
// Arrange
|
|
var expected = @"<partial>98052
|
|
|
|
</partial>
|
|
<partial2>98052
|
|
|
|
</partial2>
|
|
test-value";
|
|
|
|
// Act
|
|
var body = await Client.GetStringAsync("http://localhost/ViewEngine/ViewWithPartial");
|
|
|
|
// Assert
|
|
Assert.Equal(expected, body.Trim(), ignoreLineEndingDifferences: true);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task RazorView_DoesNotThrow_PartialViewWithEnumerableModel()
|
|
{
|
|
// Arrange
|
|
var expected = "HelloWorld";
|
|
|
|
// Act
|
|
var body = await Client.GetStringAsync(
|
|
"http://localhost/ViewEngine/ViewWithPartialTakingModelFromIEnumerable");
|
|
|
|
// Assert
|
|
Assert.Equal(expected, body.Trim());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task RazorView_PassesViewContextBetweenViewAndLayout()
|
|
{
|
|
// Arrange
|
|
var expected =
|
|
@"<title>Page title</title>
|
|
partial-contentcomponent-content";
|
|
|
|
// Act
|
|
var body = await Client.GetStringAsync("http://localhost/ViewEngine/ViewPassesViewDataToLayout");
|
|
|
|
// Assert
|
|
Assert.Equal(expected, body.Trim(), ignoreLineEndingDifferences: true);
|
|
}
|
|
|
|
public static IEnumerable<object[]> RazorViewEngine_UsesAllExpandedPathsToLookForViewsData
|
|
{
|
|
get
|
|
{
|
|
var expected1 = @"expander-index
|
|
gb-partial";
|
|
yield return new[] { "en-GB", expected1 };
|
|
|
|
var expected2 = @"fr-index
|
|
fr-partial";
|
|
yield return new[] { "fr", expected2 };
|
|
|
|
if (!TestPlatformHelper.IsMono)
|
|
{
|
|
// https://github.com/aspnet/Mvc/issues/2759
|
|
var expected3 = @"expander-index
|
|
expander-partial";
|
|
yield return new[] { "!-invalid-!", expected3 };
|
|
}
|
|
}
|
|
}
|
|
|
|
[Theory]
|
|
[MemberData(nameof(RazorViewEngine_UsesAllExpandedPathsToLookForViewsData))]
|
|
public async Task RazorViewEngine_UsesViewExpandersForViewsAndPartials(string value, string expected)
|
|
{
|
|
// Arrange
|
|
var cultureCookie = "c=" + value + "|uic=" + value;
|
|
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/TemplateExpander");
|
|
request.Headers.Add("Cookie",
|
|
new CookieHeaderValue(CookieRequestCultureProvider.DefaultCookieName, cultureCookie).ToString());
|
|
|
|
// Act
|
|
var response = await Client.SendAsync(request);
|
|
var body = await response.Content.ReadAsStringAsync();
|
|
|
|
// Assert
|
|
Assert.Equal(expected, body.Trim(), ignoreLineEndingDifferences: true);
|
|
}
|
|
|
|
public static TheoryData ViewLocationExpanders_GetIsMainPageFromContextData
|
|
{
|
|
get
|
|
{
|
|
return new TheoryData<string, string>
|
|
{
|
|
{
|
|
"Index",
|
|
"<expander-view><shared-views>/Shared-Views/ExpanderViews/_ExpanderPartial.cshtml</shared-views></expander-view>"
|
|
},
|
|
{
|
|
"Partial",
|
|
"<shared-views>/Shared-Views/ExpanderViews/_ExpanderPartial.cshtml</shared-views>"
|
|
},
|
|
};
|
|
}
|
|
}
|
|
|
|
[Theory]
|
|
[MemberData(nameof(ViewLocationExpanders_GetIsMainPageFromContextData))]
|
|
public async Task ViewLocationExpanders_GetIsMainPageFromContext(string action, string expected)
|
|
{
|
|
// Arrange & Act
|
|
var body = await Client.GetStringAsync($"http://localhost/ExpanderViews/{action}");
|
|
|
|
// Assert
|
|
Assert.Equal(expected, body.Trim());
|
|
}
|
|
|
|
public static IEnumerable<object[]> RazorViewEngine_RendersPartialViewsData
|
|
{
|
|
get
|
|
{
|
|
yield return new[]
|
|
{
|
|
"ViewWithoutLayout", "ViewWithoutLayout-Content"
|
|
};
|
|
yield return new[]
|
|
{
|
|
"PartialViewWithNamePassedIn",
|
|
@"<layout>
|
|
ViewWithLayout-Content
|
|
</layout>"
|
|
};
|
|
yield return new[]
|
|
{
|
|
"ViewWithFullPath",
|
|
@"<layout>
|
|
ViewWithFullPath-content
|
|
</layout>"
|
|
};
|
|
yield return new[]
|
|
{
|
|
"ViewWithNestedLayout",
|
|
@"<layout>
|
|
<nested-layout>
|
|
/PartialViewEngine/ViewWithNestedLayout
|
|
ViewWithNestedLayout-Content
|
|
</nested-layout>
|
|
</layout>"
|
|
};
|
|
yield return new[]
|
|
{
|
|
"PartialWithDataFromController", "<h1>hello from controller</h1>"
|
|
};
|
|
yield return new[]
|
|
{
|
|
"PartialWithModel",
|
|
@"my name is judge
|
|
<partial>98052
|
|
</partial>"
|
|
};
|
|
}
|
|
}
|
|
|
|
[Theory]
|
|
[MemberData(nameof(RazorViewEngine_RendersPartialViewsData))]
|
|
public async Task RazorViewEngine_RendersPartialViews(string actionName, string expected)
|
|
{
|
|
// Arrange & Act
|
|
var response = await Client.GetAsync("http://localhost/PartialViewEngine/" + actionName);
|
|
|
|
// Assert
|
|
await response.AssertStatusCodeAsync(HttpStatusCode.OK);
|
|
var body = await response.Content.ReadAsStringAsync();
|
|
Assert.Equal(expected, body.Trim(), ignoreLineEndingDifferences: true);
|
|
}
|
|
|
|
[Fact(Skip = "https://github.com/aspnet/Mvc/issues/8754")]
|
|
public Task RazorViewEngine_RendersViewsFromEmbeddedFileProvider_WhenLookedupByName()
|
|
=> RazorViewEngine_RendersIndexViewsFromEmbeddedFileProvider("/EmbeddedViews/LookupByName");
|
|
|
|
[Fact(Skip = "https://github.com/aspnet/Mvc/issues/8754")]
|
|
public Task RazorViewEngine_RendersViewsFromEmbeddedFileProvider_WhenLookedupByPath()
|
|
=> RazorViewEngine_RendersIndexViewsFromEmbeddedFileProvider("/EmbeddedViews/LookupByPath");
|
|
|
|
private async Task RazorViewEngine_RendersIndexViewsFromEmbeddedFileProvider(string requestPath)
|
|
{
|
|
// Arrange
|
|
var expected =
|
|
@"<embdedded-layout>Hello from EmbeddedShared/_Partial
|
|
Hello from Shared/_EmbeddedPartial
|
|
<a href=""/EmbeddedViews"">Tag Helper Link</a>
|
|
</embdedded-layout>";
|
|
|
|
// Act
|
|
var body = await Client.GetStringAsync(requestPath);
|
|
|
|
// Assert
|
|
Assert.Equal(expected, body.Trim(), ignoreLineEndingDifferences: true);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task LayoutValueIsPassedBetweenNestedViewStarts()
|
|
{
|
|
// Arrange
|
|
var expected = @"<title>viewstart-value</title>
|
|
~/Views/NestedViewStarts/NestedViewStarts/Layout.cshtml
|
|
index-content";
|
|
|
|
// Act
|
|
var body = await Client.GetStringAsync("http://localhost/NestedViewStarts");
|
|
|
|
// Assert
|
|
Assert.Equal(expected, body.Trim(), ignoreLineEndingDifferences: true);
|
|
}
|
|
|
|
public static IEnumerable<object[]> RazorViewEngine_UsesExpandersForLayoutsData
|
|
{
|
|
get
|
|
{
|
|
var expected1 =
|
|
@"<language-layout>View With Layout
|
|
</language-layout>";
|
|
|
|
yield return new[] { "en-GB", expected1 };
|
|
|
|
if (!TestPlatformHelper.IsMono)
|
|
{
|
|
// https://github.com/aspnet/Mvc/issues/2759
|
|
yield return new[] { "!-invalid-!", expected1 };
|
|
}
|
|
|
|
var expected2 =
|
|
@"<fr-language-layout>View With Layout
|
|
</fr-language-layout>";
|
|
yield return new[] { "fr", expected2 };
|
|
}
|
|
}
|
|
|
|
[Theory]
|
|
[MemberData(nameof(RazorViewEngine_UsesExpandersForLayoutsData))]
|
|
public async Task RazorViewEngine_UsesExpandersForLayouts(string value, string expected)
|
|
{
|
|
// Arrange
|
|
var cultureCookie = "c=" + value + "|uic=" + value;
|
|
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/TemplateExpander/ViewWithLayout");
|
|
request.Headers.Add("Cookie",
|
|
new CookieHeaderValue(CookieRequestCultureProvider.DefaultCookieName, cultureCookie).ToString());
|
|
|
|
// Act
|
|
var response = await Client.SendAsync(request);
|
|
var body = await response.Content.ReadAsStringAsync();
|
|
|
|
// Assert
|
|
Assert.Equal(expected, body.Trim(), ignoreLineEndingDifferences: true);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ViewStartsCanUseDirectivesInjectedFromParentGlobals()
|
|
{
|
|
// Arrange
|
|
var expected =
|
|
@"<view-start>Hello Controller-Person</view-start>
|
|
<page>Hello Controller-Person</page>";
|
|
var target = "http://localhost/NestedViewImports";
|
|
|
|
// Act
|
|
var body = await Client.GetStringAsync(target);
|
|
|
|
// Assert
|
|
Assert.Equal(expected, body.Trim(), ignoreLineEndingDifferences: true);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ViewComponentsExecuteLayout()
|
|
{
|
|
// Arrange
|
|
var expected =
|
|
@"<title>View With Component With Layout</title>
|
|
Page Content
|
|
<component-title>ViewComponent With Title</component-title>
|
|
<component-body>Component With Layout</component-body>";
|
|
|
|
// Act
|
|
var body = await Client.GetStringAsync("http://localhost/ViewEngine/ViewWithComponentThatHasLayout");
|
|
|
|
// Assert
|
|
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]
|
|
public async Task ViewComponentsDoNotExecuteViewStarts()
|
|
{
|
|
// Arrange
|
|
var expected = @"<page-content>ViewComponent With ViewStart</page-content>";
|
|
|
|
// Act
|
|
var body = await Client.GetStringAsync("http://localhost/ViewEngine/ViewWithComponentThatHasViewStart");
|
|
|
|
// Assert
|
|
Assert.Equal(expected, body.Trim());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task PartialDoNotExecuteViewStarts()
|
|
{
|
|
// Arrange
|
|
var expected = "Partial that does not specify Layout";
|
|
|
|
// Act
|
|
var body = await Client.GetStringAsync("http://localhost/PartialsWithLayout/PartialDoesNotExecuteViewStarts");
|
|
|
|
// Assert
|
|
Assert.Equal(expected, body.Trim());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task PartialsRenderedViaRenderPartialAsync_CanRenderLayouts()
|
|
{
|
|
// Arrange
|
|
var expected =
|
|
@"<layout-for-viewstart-with-layout><layout-for-viewstart-with-layout>Partial that specifies Layout
|
|
</layout-for-viewstart-with-layout>Partial that does not specify Layout</layout-for-viewstart-with-layout>";
|
|
|
|
// Act
|
|
var body = await Client.GetStringAsync("http://localhost/PartialsWithLayout/PartialsRenderedViaRenderPartial");
|
|
|
|
// Assert
|
|
Assert.Equal(expected, body.Trim(), ignoreLineEndingDifferences: true);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task PartialsRenderedViaPartialAsync_CanRenderLayouts()
|
|
{
|
|
// Arrange
|
|
var expected =
|
|
@"<layout-for-viewstart-with-layout><layout-for-viewstart-with-layout>Partial that specifies Layout
|
|
</layout-for-viewstart-with-layout>
|
|
Partial that does not specify Layout
|
|
</layout-for-viewstart-with-layout>";
|
|
|
|
// Act
|
|
var body = await Client.GetStringAsync("http://localhost/PartialsWithLayout/PartialsRenderedViaPartialAsync");
|
|
|
|
// Assert
|
|
Assert.Equal(expected, body.Trim(), ignoreLineEndingDifferences: true);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task RazorView_SetsViewPathAndExecutingPagePath()
|
|
{
|
|
// Arrange
|
|
var outputFile = "compiler/resources/ViewEngineController.ViewWithPaths.txt";
|
|
var expectedContent = await ResourceFile.ReadResourceAsync(_assembly, outputFile, sourceFile: false);
|
|
|
|
// Act
|
|
var responseContent = await Client.GetStringAsync("http://localhost/ViewWithPaths");
|
|
|
|
// Assert
|
|
responseContent = responseContent.Trim();
|
|
#if GENERATE_BASELINES
|
|
ResourceFile.UpdateFile(_assembly, outputFile, expectedContent, responseContent);
|
|
#else
|
|
Assert.Equal(
|
|
expectedContent,
|
|
responseContent,
|
|
ignoreLineEndingDifferences: true);
|
|
#endif
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ViewEngine_NormalizesPathsReturnedByViewLocationExpanders()
|
|
{
|
|
// Arrange
|
|
var expected =
|
|
@"Layout
|
|
Page
|
|
Partial";
|
|
|
|
// Act
|
|
var responseContent = await Client.GetStringAsync("/BackSlash");
|
|
|
|
// Assert
|
|
Assert.Equal(expected, responseContent, ignoreLineEndingDifferences: true);
|
|
}
|
|
|
|
[Fact(Skip = "https://github.com/aspnet/Mvc/issues/8754")]
|
|
public async Task ViewEngine_ResolvesPathsWithSlashesThatDoNotHaveExtensions()
|
|
{
|
|
// Arrange
|
|
var expected = @"<embdedded-layout>Hello from EmbeddedHome\EmbeddedPartial</embdedded-layout>";
|
|
|
|
// Act
|
|
var responseContent = await Client.GetStringAsync("/EmbeddedViews/RelativeNonPath");
|
|
|
|
// Assert
|
|
Assert.Equal(expected, responseContent.Trim());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ViewEngine_DiscoversViewsFromPagesSharedDirectory()
|
|
{
|
|
// Arrange
|
|
var expected = "Hello from Pages/Shared";
|
|
|
|
// Act
|
|
var responseContent = await Client.GetStringAsync("/ViewEngine/SearchInPages");
|
|
|
|
// Assert
|
|
Assert.Equal(expected, responseContent.Trim());
|
|
}
|
|
}
|
|
}
|