// 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.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> { private static readonly Assembly _assembly = typeof(ViewEngineTests).GetTypeInfo().Assembly; public ViewEngineTests(MvcTestFixture fixture) { Client = fixture.CreateDefaultClient(); } public HttpClient Client { get; } public static IEnumerable RazorView_ExecutesPageAndLayoutData { get { yield return new[] { "ViewWithoutLayout", @"ViewWithoutLayout-Content" }; yield return new[] { "ViewWithLayout", @" ViewWithLayout-Content " }; yield return new[] { "ViewWithFullPath", @" ViewWithFullPath-content " }; yield return new[] { "ViewWithNestedLayout", @" /ViewEngine/ViewWithNestedLayout ViewWithNestedLayout-Content " }; yield return new[] { "ViewWithDataFromController", "

hello from controller

" }; } } [Theory] [MemberData(nameof(RazorView_ExecutesPageAndLayoutData))] public async Task RazorView_ExecutesPageAndLayout(string actionName, string expected) { // Arrange & Act var body = await Client.GetStringAsync("http://localhost/ViewEngine/" + actionName); // Assert Assert.Equal(expected, body.Trim(), ignoreLineEndingDifferences: true); } [Fact] public async Task RazorView_ExecutesPartialPagesWithCorrectContext() { // Arrange var expected = @"98052 98052 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 = @"Page 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 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 { { "Index", "/Shared-Views/ExpanderViews/_ExpanderPartial.cshtml" }, { "Partial", "/Shared-Views/ExpanderViews/_ExpanderPartial.cshtml" }, }; } } [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 RazorViewEngine_RendersPartialViewsData { get { yield return new[] { "ViewWithoutLayout", "ViewWithoutLayout-Content" }; yield return new[] { "PartialViewWithNamePassedIn", @" ViewWithLayout-Content " }; yield return new[] { "ViewWithFullPath", @" ViewWithFullPath-content " }; yield return new[] { "ViewWithNestedLayout", @" /PartialViewEngine/ViewWithNestedLayout ViewWithNestedLayout-Content " }; yield return new[] { "PartialWithDataFromController", "

hello from controller

" }; yield return new[] { "PartialWithModel", @"my name is judge 98052 " }; } } [Theory] [MemberData(nameof(RazorViewEngine_RendersPartialViewsData))] public async Task RazorViewEngine_RendersPartialViews(string actionName, string expected) { // Arrange & Act var body = await Client.GetStringAsync("http://localhost/PartialViewEngine/" + actionName); // Assert Assert.Equal(expected, body.Trim(), ignoreLineEndingDifferences: true); } [Fact] public Task RazorViewEngine_RendersViewsFromEmbeddedFileProvider_WhenLookedupByName() => RazorViewEngine_RendersIndexViewsFromEmbeddedFileProvider("/EmbeddedViews/LookupByName"); [Fact] public Task RazorViewEngine_RendersViewsFromEmbeddedFileProvider_WhenLookedupByPath() => RazorViewEngine_RendersIndexViewsFromEmbeddedFileProvider("/EmbeddedViews/LookupByPath"); private async Task RazorViewEngine_RendersIndexViewsFromEmbeddedFileProvider(string requestPath) { // Arrange var expected = @"Hello from EmbeddedShared/_Partial Hello from Shared/_EmbeddedPartial Tag Helper Link "; // Act var body = await Client.GetStringAsync(requestPath); // Assert Assert.Equal(expected, body.Trim(), ignoreLineEndingDifferences: true); } [Fact] public async Task LayoutValueIsPassedBetweenNestedViewStarts() { // Arrange var expected = @"viewstart-value ~/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 RazorViewEngine_UsesExpandersForLayoutsData { get { var expected1 = @"View With Layout "; yield return new[] { "en-GB", expected1 }; if (!TestPlatformHelper.IsMono) { // https://github.com/aspnet/Mvc/issues/2759 yield return new[] { "!-invalid-!", expected1 }; } var expected2 = @"View With 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 = @"Hello Controller-Person Hello Controller-Person"; 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 = @"View With Component With Layout Page Content ViewComponent With Title Component With Layout"; // 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 = @" /ViewEngine/ViewWithRelativePath ViewWithRelativePath-content partial-content View with relative path title Component with Relative Path WriteLiteral says:Write says:98052WriteLiteral says: "; // Act var body = await Client.GetStringAsync("http://localhost/ViewEngine/ViewWithRelativePath"); // Assert Assert.Equal(expected, body.Trim(), ignoreLineEndingDifferences: true); } [Fact] public async Task ViewComponentsDoNotExecuteViewStarts() { // Arrange var expected = @"ViewComponent With ViewStart"; // 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 = @"Partial that specifies Layout Partial that does not specify 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 = @"Partial that specifies Layout Partial that does not specify 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] public async Task ViewEngine_ResolvesPathsWithSlashesThatDoNotHaveExtensions() { // Arrange var expected = @"Hello from EmbeddedHome\EmbeddedPartial"; // 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()); } } }