From 2570126b273f643f96218461a4686223f18134c4 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Mon, 29 Apr 2019 09:32:48 -0700 Subject: [PATCH] Fixup XUnit warnings when running MVC tests (#9823) --- .../RazorPageCreateModelExpressionTest.cs | 270 ++++++++++++------ src/Mvc/Mvc.Razor/test/RazorPageTest.cs | 235 +++++++++------ src/Mvc/Mvc.Razor/test/RazorViewEngineTest.cs | 180 +++++++++--- .../TagHelpers/UrlResolutionTagHelperTest.cs | 28 +- 4 files changed, 488 insertions(+), 225 deletions(-) diff --git a/src/Mvc/Mvc.Razor/test/RazorPageCreateModelExpressionTest.cs b/src/Mvc/Mvc.Razor/test/RazorPageCreateModelExpressionTest.cs index de2ceb9dd0..d510595c73 100644 --- a/src/Mvc/Mvc.Razor/test/RazorPageCreateModelExpressionTest.cs +++ b/src/Mvc/Mvc.Razor/test/RazorPageCreateModelExpressionTest.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using System.Linq.Expressions; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Abstractions; @@ -20,73 +19,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor { public class RazorPageCreateModelExpressionTest { - public static TheoryData IdentityExpressions - { - get - { - return new TheoryData, string> - { - // m => m - { page => page.CreateModelExpression1(), string.Empty }, - // m => Model - { page => page.CreateModelExpression2(), string.Empty }, - }; - } - } - - public static TheoryData NotQuiteIdentityExpressions - { - get - { - return new TheoryData, string, Type> - { - // m => m.Model - { page => page.CreateModelExpression1(), "Model", typeof(RecursiveModel) }, - // m => ViewData.Model - { page => page.CreateModelExpression2(), "ViewData.Model", typeof(RecursiveModel) }, - // m => ViewContext.ViewData.Model - // This property has type object because ViewData is not exposed as ViewDataDictionary. - { page => page.CreateModelExpression3(), "ViewContext.ViewData.Model", typeof(object) }, - }; - } - } - - public static TheoryData>, string> IntExpressions - { - get - { - var somethingElse = 23; - return new TheoryData>, string> - { - { model => somethingElse, "somethingElse" }, - { model => model.Id, "Id" }, - { model => model.SubModel.Id, "SubModel.Id" }, - { model => model.SubModel.SubSubModel.Id, "SubModel.SubSubModel.Id" }, - }; - } - } - - public static TheoryData>, string> StringExpressions - { - get - { - var somethingElse = "This is something else"; - return new TheoryData>, string> - { - { model => somethingElse, "somethingElse" }, - { model => model.Name, "Name" }, - { model => model.SubModel.Name, "SubModel.Name" }, - { model => model.SubModel.SubSubModel.Name, "SubModel.SubSubModel.Name" }, - }; - } - } - - [Theory] - [MemberData(nameof(IdentityExpressions))] - public void CreateModelExpression_ReturnsExpectedMetadata_IdentityExpressions( - Func createModelExpression, - string expectedName) + [Fact] + public void CreateModelExpression_ReturnsExpectedMetadata_IdentityExpressions_ForModelGivesM() { + // m => m // Arrange var viewContext = CreateViewContext(); var modelExplorer = viewContext.ViewData.ModelExplorer.GetExplorerForProperty( @@ -100,22 +36,78 @@ namespace Microsoft.AspNetCore.Mvc.Razor var page = CreateIdentityPage(viewContext); // Act - var modelExpression = createModelExpression(page); + var modelExpression = page.CreateModelExpression1(); // Assert Assert.NotNull(modelExpression); - Assert.Equal(expectedName, modelExpression.Name); + Assert.Empty(modelExpression.Name); Assert.Same(modelExplorer, modelExpression.ModelExplorer); } - [Theory] - [MemberData(nameof(NotQuiteIdentityExpressions))] - public void CreateModelExpression_ReturnsExpectedMetadata_NotQuiteIdentityExpressions( + [Fact] + public void CreateModelExpression_ReturnsExpectedMetadata_IdentityExpressions_ForModelGivesModel() + { + // m => m.Model + // Arrange + var viewContext = CreateViewContext(); + var modelExplorer = viewContext.ViewData.ModelExplorer.GetExplorerForProperty( + nameof(RazorPageCreateModelExpressionModel.Name)); + var viewData = new ViewDataDictionary(viewContext.ViewData) + { + ModelExplorer = modelExplorer, + }; + viewContext.ViewData = viewData; + + var page = CreateIdentityPage(viewContext); + + // Act + var modelExpression = page.CreateModelExpression2(); + + // Assert + Assert.NotNull(modelExpression); + Assert.Empty(modelExpression.Name); + Assert.Same(modelExplorer, modelExpression.ModelExplorer); + } + + [Fact] + public void CreateModelExpression_ReturnsExpectedMetadata_NotQuiteIdentityExpressions_ForModelGivesMDotModel() + { + // m => m.Model + // Arrange + var expectedName = "Model"; + var expectedType = typeof(RecursiveModel); + + CreateModelExpression_NotQuiteIdentityExpressions(page => page.CreateModelExpression1(), expectedName, expectedType); + } + + [Fact] + public void CreateModelExpression_ReturnsExpectedMetadata_NotQuiteIdentityExpressions_ForModelGivesViewDataDotModel() + { + // m => ViewData.Model + // Arrange + var expectedName = "ViewData.Model"; + var expectedType = typeof(RecursiveModel); + + CreateModelExpression_NotQuiteIdentityExpressions(page => page.CreateModelExpression2(), expectedName, expectedType); + } + + [Fact] + public void CreateModelExpression_ReturnsExpectedMetadata_NotQuiteIdentityExpressions_ForModelGivesViewContextDotViewDataDotModel() + { + // m => ViewContext.ViewData.Model + // Arrange + var expectedName = "ViewContext.ViewData.Model"; + // This property has type object because ViewData is not exposed as ViewDataDictionary. + var expectedType = typeof(object); + + CreateModelExpression_NotQuiteIdentityExpressions(page => page.CreateModelExpression3(), expectedName, expectedType); + } + + private static void CreateModelExpression_NotQuiteIdentityExpressions( Func createModelExpression, string expectedName, Type expectedType) { - // Arrange var viewContext = CreateViewContext(); var viewData = new ViewDataDictionary(viewContext.ViewData); viewContext.ViewData = viewData; @@ -136,38 +128,144 @@ namespace Microsoft.AspNetCore.Mvc.Razor Assert.Equal(expectedType, modelExpression.Metadata.ModelType); } - [Theory] - [MemberData(nameof(IntExpressions))] - public void CreateModelExpression_ReturnsExpectedMetadata_IntExpressions( - Expression> expression, - string expectedName) + [Fact] + public void CreateModelExpression_ReturnsExpectedMetadata_IntExpressions_ForModelGivesSomethingElse() { // Arrange + var expected = "somethingElse"; + var somethingElse = 23; var viewContext = CreateViewContext(); var page = CreatePage(viewContext); // Act - var result = page.ModelExpressionProvider.CreateModelExpression(page.ViewData, expression); + var result = page.ModelExpressionProvider.CreateModelExpression(page.ViewData, model => somethingElse); // Assert Assert.NotNull(result); Assert.NotNull(result.Metadata); Assert.Equal(typeof(int), result.Metadata.ModelType); - Assert.Equal(expectedName, result.Name); + Assert.Equal(expected, result.Name); } - [Theory] - [MemberData(nameof(StringExpressions))] - public void CreateModelExpression_ReturnsExpectedMetadata_StringExpressions( - Expression> expression, - string expectedName) + [Fact] + public void CreateModelExpression_ReturnsExpectedMetadata_IntExpressions_ForModelGivesId() { // Arrange + var expected = "Id"; var viewContext = CreateViewContext(); var page = CreatePage(viewContext); // Act - var result = page.ModelExpressionProvider.CreateModelExpression(page.ViewData, expression); + var result = page.ModelExpressionProvider.CreateModelExpression(page.ViewData, model => model.Id); + + // Assert + Assert.NotNull(result); + Assert.NotNull(result.Metadata); + Assert.Equal(typeof(int), result.Metadata.ModelType); + Assert.Equal(expected, result.Name); + } + + [Fact] + public void CreateModelExpression_ReturnsExpectedMetadata_IntExpressions_ForModelGivesSubModelId() + { + // Arrange + var expected = "SubModel.Id"; + var viewContext = CreateViewContext(); + var page = CreatePage(viewContext); + + // Act + var result = page.ModelExpressionProvider.CreateModelExpression(page.ViewData, model => model.SubModel.Id); + + // Assert + Assert.NotNull(result); + Assert.NotNull(result.Metadata); + Assert.Equal(typeof(int), result.Metadata.ModelType); + Assert.Equal(expected, result.Name); + } + + [Fact] + public void CreateModelExpression_ReturnsExpectedMetadata_IntExpressions_ForModelGivesSubSubModelId() + { + // Arrange + var expected = "SubModel.SubSubModel.Id"; + var viewContext = CreateViewContext(); + var page = CreatePage(viewContext); + + // Act + var result = page.ModelExpressionProvider.CreateModelExpression(page.ViewData, model => model.SubModel.SubSubModel.Id); + + // Assert + Assert.NotNull(result); + Assert.NotNull(result.Metadata); + Assert.Equal(typeof(int), result.Metadata.ModelType); + Assert.Equal(expected, result.Name); + } + + [Fact] + public void CreateModelExpression_ReturnsExpectedMetadata_StringExpressions_ForModelGivesSomethingElse() + { + // Arrange + var somethingElse = "This is something else"; + var expectedName = "somethingElse"; + var viewContext = CreateViewContext(); + var page = CreatePage(viewContext); + + // Act + var result = page.ModelExpressionProvider.CreateModelExpression(page.ViewData, model => somethingElse); + + // Assert + Assert.NotNull(result); + Assert.NotNull(result.Metadata); + Assert.Equal(typeof(string), result.Metadata.ModelType); + Assert.Equal(expectedName, result.Name); + } + + [Fact] + public void CreateModelExpression_ReturnsExpectedMetadata_StringExpressions_ForModelGivesName() + { + // Arrange + var expectedName = "Name"; + var viewContext = CreateViewContext(); + var page = CreatePage(viewContext); + + // Act + var result = page.ModelExpressionProvider.CreateModelExpression(page.ViewData, model => model.Name); + + // Assert + Assert.NotNull(result); + Assert.NotNull(result.Metadata); + Assert.Equal(typeof(string), result.Metadata.ModelType); + Assert.Equal(expectedName, result.Name); + } + + [Fact] + public void CreateModelExpression_ReturnsExpectedMetadata_StringExpressions_ForModelGivesSubmodelName() + { + // Arrange + var expectedName = "SubModel.SubSubModel.Name"; + var viewContext = CreateViewContext(); + var page = CreatePage(viewContext); + + // Act + var result = page.ModelExpressionProvider.CreateModelExpression(page.ViewData, model => model.SubModel.SubSubModel.Name); + + // Assert + Assert.NotNull(result); + Assert.NotNull(result.Metadata); + Assert.Equal(typeof(string), result.Metadata.ModelType); + Assert.Equal(expectedName, result.Name); + } + + [Fact] + public void CreateModelExpression_ReturnsExpectedMetadata_StringExpressions_ForModelGivesSubSubmodelName() + { + // Arrange + var expectedName = "SubModel.Name"; + var viewContext = CreateViewContext(); + var page = CreatePage(viewContext); + + // Act + var result = page.ModelExpressionProvider.CreateModelExpression(page.ViewData, model => model.SubModel.Name); // Assert Assert.NotNull(result); diff --git a/src/Mvc/Mvc.Razor/test/RazorPageTest.cs b/src/Mvc/Mvc.Razor/test/RazorPageTest.cs index c94f921f3a..8475c8a2fc 100644 --- a/src/Mvc/Mvc.Razor/test/RazorPageTest.cs +++ b/src/Mvc/Mvc.Razor/test/RazorPageTest.cs @@ -140,24 +140,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor ex.Message); } - public static TheoryData HtmlEncoderData - { - get - { - return new TheoryData - { - HtmlEncoder.Default, - NullHtmlEncoder.Default, - new HtmlTestEncoder(), - }; - } - } - - [Theory] - [MemberData(nameof(HtmlEncoderData))] - public async Task StartTagHelperWritingScope_SetsHtmlEncoder(HtmlEncoder encoder) + [Fact] + public async Task StartTagHelperWritingScope_SetsHtmlEncoder() { // Arrange + var encoder = Mock.Of(); var page = CreatePage(v => { v.StartTagHelperWritingScope(encoder); @@ -1176,53 +1163,20 @@ namespace Microsoft.AspNetCore.Mvc.Razor get { // attributeValues, expectedValue - return new TheoryData[], string> + return new TheoryData { { - new [] - { - Tuple.Create(string.Empty, 9, (object)"Hello", 9, true), - }, - "Hello" + string.Empty, 9, (object)"Hello", 9, true, "Hello" }, { - new [] - { - Tuple.Create(" ", 9, (object)"Hello", 10, true) - }, - " Hello" + " ", 9, (object)"Hello", 10, true, " Hello" }, { - new [] - { - Tuple.Create(" ", 9, (object)null, 10, false) - }, - string.Empty + " ", 9, (object)null, 10, false, string.Empty }, { - new [] - { - Tuple.Create(" ", 9, (object)false, 10, false) - }, - " HtmlEncode[[False]]" - }, - { - new [] - { - Tuple.Create(" ", 9, (object)true, 11, false), - Tuple.Create(" ", 9, (object)"abcd", 17, true) - }, - " HtmlEncode[[True]] abcd" - }, - { - new [] - { - Tuple.Create(string.Empty, 9, (object)"prefix", 9, true), - Tuple.Create(" ", 15, (object)null, 17, false), - Tuple.Create(" ", 21, (object)"suffix", 22, false), - }, - "prefix HtmlEncode[[suffix]]" + " ", 9, (object)false, 10, false, " HtmlEncode[[False]]" }, }; } @@ -1231,7 +1185,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor [Theory] [MemberData(nameof(AddHtmlAttributeValues_ValueData))] public void AddHtmlAttributeValues_AddsToHtmlAttributesAsExpected( - Tuple[] attributeValues, + string prefix, + int prefixOffset, + object value, + int valueOffset, + bool isLiteral, string expectedValue) { // Arrange @@ -1246,6 +1204,94 @@ namespace Microsoft.AspNetCore.Mvc.Razor startTagHelperWritingScope: _ => { }, endTagHelperWritingScope: () => new DefaultTagHelperContent()); + // Act + page.BeginAddHtmlAttributeValues(executionContext, "someattr", 1, HtmlAttributeValueStyle.SingleQuotes); + page.AddHtmlAttributeValue(prefix, prefixOffset, value, valueOffset, 0, isLiteral); + page.EndAddHtmlAttributeValues(executionContext); + + // Assert + var output = executionContext.Output; + var htmlAttribute = Assert.Single(output.Attributes); + Assert.Equal("someattr", htmlAttribute.Name, StringComparer.Ordinal); + var htmlContent = Assert.IsAssignableFrom(htmlAttribute.Value); + Assert.Equal(expectedValue, HtmlContentUtilities.HtmlContentToString(htmlContent), StringComparer.Ordinal); + Assert.Equal(HtmlAttributeValueStyle.SingleQuotes, htmlAttribute.ValueStyle); + + var context = executionContext.Context; + var allAttribute = Assert.Single(context.AllAttributes); + Assert.Equal("someattr", allAttribute.Name, StringComparer.Ordinal); + htmlContent = Assert.IsAssignableFrom(allAttribute.Value); + Assert.Equal(expectedValue, HtmlContentUtilities.HtmlContentToString(htmlContent), StringComparer.Ordinal); + Assert.Equal(HtmlAttributeValueStyle.SingleQuotes, allAttribute.ValueStyle); + } + + [Fact] + public void AddHtmlAttributeValues_TwoAttributeValues_AddsToHtmlAttributesAsExpected() + { + // Arrange + var expectedValue = " HtmlEncode[[True]] abcd"; + var attributeValues = new[] + { + Tuple.Create(" ", 9, (object)true, 11, false), + Tuple.Create(" ", 9, (object)"abcd", 17, true) + }; + var page = CreatePage(p => { }); + page.HtmlEncoder = new HtmlTestEncoder(); + var executionContext = new TagHelperExecutionContext( + "p", + tagMode: TagMode.StartTagAndEndTag, + items: new Dictionary(), + uniqueId: string.Empty, + executeChildContentAsync: () => Task.FromResult(result: true), + startTagHelperWritingScope: _ => { }, + endTagHelperWritingScope: () => new DefaultTagHelperContent()); + + // Act + page.BeginAddHtmlAttributeValues(executionContext, "someattr", attributeValues.Length, HtmlAttributeValueStyle.SingleQuotes); + foreach (var value in attributeValues) + { + page.AddHtmlAttributeValue(value.Item1, value.Item2, value.Item3, value.Item4, 0, value.Item5); + } + page.EndAddHtmlAttributeValues(executionContext); + + // Assert + var output = executionContext.Output; + var htmlAttribute = Assert.Single(output.Attributes); + Assert.Equal("someattr", htmlAttribute.Name, StringComparer.Ordinal); + var htmlContent = Assert.IsAssignableFrom(htmlAttribute.Value); + Assert.Equal(expectedValue, HtmlContentUtilities.HtmlContentToString(htmlContent), StringComparer.Ordinal); + Assert.Equal(HtmlAttributeValueStyle.SingleQuotes, htmlAttribute.ValueStyle); + + var context = executionContext.Context; + var allAttribute = Assert.Single(context.AllAttributes); + Assert.Equal("someattr", allAttribute.Name, StringComparer.Ordinal); + htmlContent = Assert.IsAssignableFrom(allAttribute.Value); + Assert.Equal(expectedValue, HtmlContentUtilities.HtmlContentToString(htmlContent), StringComparer.Ordinal); + Assert.Equal(HtmlAttributeValueStyle.SingleQuotes, allAttribute.ValueStyle); + } + + [Fact] + public void AddHtmlAttributeValues_ThreeAttributeValues_AddsToHtmlAttributesAsExpected() + { + // Arrange + var expectedValue = "prefix HtmlEncode[[suffix]]"; + var attributeValues = new[] + { + Tuple.Create(string.Empty, 9, (object)"prefix", 9, true), + Tuple.Create(" ", 15, (object)null, 17, false), + Tuple.Create(" ", 21, (object)"suffix", 22, false), + }; + var page = CreatePage(p => { }); + page.HtmlEncoder = new HtmlTestEncoder(); + var executionContext = new TagHelperExecutionContext( + "p", + tagMode: TagMode.StartTagAndEndTag, + items: new Dictionary(), + uniqueId: string.Empty, + executeChildContentAsync: () => Task.FromResult(result: true), + startTagHelperWritingScope: _ => { }, + endTagHelperWritingScope: () => new DefaultTagHelperContent()); + // Act page.BeginAddHtmlAttributeValues(executionContext, "someattr", attributeValues.Length, HtmlAttributeValueStyle.SingleQuotes); foreach (var value in attributeValues) @@ -1342,50 +1388,22 @@ namespace Microsoft.AspNetCore.Mvc.Razor get { // AttributeValues, ExpectedOutput - return new TheoryData[], string> + return new TheoryData { { - new[] - { - Tuple.Create(string.Empty, 9, (object)true, 9, false), - }, - "someattr=HtmlEncode[[someattr]]" + string.Empty, 9, (object)true, 9, false, "someattr=HtmlEncode[[someattr]]" }, { - new[] - { - Tuple.Create(string.Empty, 9, (object)false, 9, false), - }, - string.Empty + string.Empty, 9, (object)false, 9, false, string.Empty }, { - new[] - { - Tuple.Create(string.Empty, 9, (object)null, 9, false), - }, - string.Empty + string.Empty, 9, (object)null, 9, false, string.Empty }, { - new[] - { - Tuple.Create(" ", 9, (object)false, 11, false), - }, - "someattr= HtmlEncode[[False]]" + " ", 9, (object)false, 11, false, "someattr= HtmlEncode[[False]]" }, { - new[] - { - Tuple.Create(" ", 9, (object)null, 11, false), - }, - "someattr=" - }, - { - new[] - { - Tuple.Create(" ", 9, (object)true, 11, false), - Tuple.Create(" ", 15, (object)"abcd", 17, true), - }, - "someattr= HtmlEncode[[True]] abcd" + " ", 9, (object)null, 11, false, "someattr=" }, }; } @@ -1394,10 +1412,47 @@ namespace Microsoft.AspNetCore.Mvc.Razor [Theory] [MemberData(nameof(WriteAttributeData))] public void WriteAttribute_UsesSpecifiedWriter_WritesAsExpected( - Tuple[] attributeValues, + string prefix, + int prefixOffset, + object value, + int valueOffset, + bool isLiteral, string expectedOutput) { // Arrange + var page = CreatePage(p => { }); + page.HtmlEncoder = new HtmlTestEncoder(); + var writer = new StringWriter(); + var suffix = string.Empty; + + // Act + page.PushWriter(writer); + page.BeginWriteAttribute("someattr", "someattr=", 0, suffix, 0, 1); + page.WriteAttributeValue( + prefix, + prefixOffset, + value, + valueOffset, + value?.ToString().Length ?? 0, + isLiteral); + page.EndWriteAttribute(); + page.PopWriter(); + + // Assert + Assert.Equal(expectedOutput, writer.ToString()); + } + + [Fact] + public void WriteAttribute_MultipleValues_UsesSpecifiedWriter_WritesAsExpected() + { + // Arrange + var attributeValues = new[] + { + Tuple.Create(" ", 9, (object)true, 11, false), + Tuple.Create(" ", 15, (object)"abcd", 17, true), + }; + var expectedOutput = "someattr= HtmlEncode[[True]] abcd"; + var page = CreatePage(p => { }); page.HtmlEncoder = new HtmlTestEncoder(); var writer = new StringWriter(); diff --git a/src/Mvc/Mvc.Razor/test/RazorViewEngineTest.cs b/src/Mvc/Mvc.Razor/test/RazorViewEngineTest.cs index dea493c874..7611c20fea 100644 --- a/src/Mvc/Mvc.Razor/test/RazorViewEngineTest.cs +++ b/src/Mvc/Mvc.Razor/test/RazorViewEngineTest.cs @@ -52,32 +52,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor } } - public static IEnumerable ViewLocationExpanderTestData - { - get - { - yield return new object[] - { - _controllerTestContext, - new[] - { - "/Views/{1}/{0}.cshtml", - "/Views/Shared/{0}.cshtml" - } - }; - yield return new object[] - { - _areaTestContext, - new[] - { - "/Areas/{2}/Views/{1}/{0}.cshtml", - "/Areas/{2}/Views/Shared/{0}.cshtml", - "/Views/Shared/{0}.cshtml" - } - }; - } - } [Theory] [InlineData(null)] @@ -681,13 +656,85 @@ namespace Microsoft.AspNetCore.Mvc.Razor Assert.Same(areaPage2, view4.RazorPage); } - [Theory] - [MemberData(nameof(ViewLocationExpanderTestData))] - public void FindView_UsesViewLocationExpandersToLocateViews( - IDictionary routeValues, - IEnumerable expectedSeeds) + [Fact] + public void FindView_UsesViewLocationExpandersToLocateViews() { // Arrange + var routeValues = _controllerTestContext; + var expectedSeeds = new[] + { + "/Views/{1}/{0}.cshtml", + "/Views/Shared/{0}.cshtml" + }; + + var pageFactory = new Mock(); + pageFactory + .Setup(p => p.CreateFactory("test-string/bar.cshtml")) + .Returns(GetPageFactoryResult(() => Mock.Of())) + .Verifiable(); + + var expander1Result = new[] { "some-seed" }; + var expander1 = new Mock(); + expander1 + .Setup(e => e.PopulateValues(It.IsAny())) + .Callback((ViewLocationExpanderContext c) => + { + Assert.NotNull(c.ActionContext); + c.Values["expander-key"] = expander1.ToString(); + }) + .Verifiable(); + expander1 + .Setup(e => e.ExpandViewLocations( + It.IsAny(), + It.IsAny>())) + .Callback((ViewLocationExpanderContext c, IEnumerable seeds) => + { + Assert.NotNull(c.ActionContext); + Assert.Equal(expectedSeeds, seeds); + }) + .Returns(expander1Result) + .Verifiable(); + + var expander2 = new Mock(); + expander2 + .Setup(e => e.ExpandViewLocations( + It.IsAny(), + It.IsAny>())) + .Callback((ViewLocationExpanderContext c, IEnumerable seeds) => + { + Assert.Equal(expander1Result, seeds); + }) + .Returns(new[] { "test-string/{1}.cshtml" }) + .Verifiable(); + + var viewEngine = CreateViewEngine( + pageFactory.Object, + new[] { expander1.Object, expander2.Object }); + var context = GetActionContext(routeValues); + + // Act + var result = viewEngine.FindView(context, "test-view", isMainPage: true); + + // Assert + Assert.True(result.Success); + Assert.IsAssignableFrom(result.View); + pageFactory.Verify(); + expander1.Verify(); + expander2.Verify(); + } + + [Fact] + public void FindView_UsesViewLocationExpandersToLocateViews_ForAreas() + { + // Arrange + var routeValues = _areaTestContext; + var expectedSeeds = new[] + { + "/Areas/{2}/Views/{1}/{0}.cshtml", + "/Areas/{2}/Views/Shared/{0}.cshtml", + "/Views/Shared/{0}.cshtml" + }; + var pageFactory = new Mock(); pageFactory .Setup(p => p.CreateFactory("test-string/bar.cshtml")) @@ -1092,13 +1139,76 @@ namespace Microsoft.AspNetCore.Mvc.Razor "pageName"); } - [Theory] - [MemberData(nameof(ViewLocationExpanderTestData))] - public void FindPage_UsesViewLocationExpander_ToExpandPaths( - IDictionary routeValues, - IEnumerable expectedSeeds) + [Fact] + public void FindPage_UsesViewLocationExpander_ToExpandPaths() { // Arrange + var routeValues = _controllerTestContext; + var expectedSeeds = new[] + { + "/Views/{1}/{0}.cshtml", + "/Views/Shared/{0}.cshtml" + }; + + var page = Mock.Of(); + var pageFactory = new Mock(); + pageFactory + .Setup(p => p.CreateFactory("expanded-path/bar-layout")) + .Returns(GetPageFactoryResult(() => page)) + .Verifiable(); + + var expander = new Mock(); + expander + .Setup(e => e.PopulateValues(It.IsAny())) + .Callback((ViewLocationExpanderContext c) => + { + Assert.NotNull(c.ActionContext); + c.Values["expander-key"] = expander.ToString(); + }) + .Verifiable(); + expander + .Setup(e => e.ExpandViewLocations( + It.IsAny(), + It.IsAny>())) + .Returns((ViewLocationExpanderContext c, IEnumerable seeds) => + { + Assert.NotNull(c.ActionContext); + Assert.Equal(expectedSeeds, seeds); + + Assert.Equal(expander.ToString(), c.Values["expander-key"]); + + return new[] { "expanded-path/bar-{0}" }; + }) + .Verifiable(); + + var viewEngine = CreateViewEngine( + pageFactory.Object, + new[] { expander.Object }); + var context = GetActionContext(routeValues); + + // Act + var result = viewEngine.FindPage(context, "layout"); + + // Assert + Assert.Equal("layout", result.Name); + Assert.Same(page, result.Page); + Assert.Null(result.SearchedLocations); + pageFactory.Verify(); + expander.Verify(); + } + + [Fact] + public void FindPage_UsesViewLocationExpander_ToExpandPaths_ForAreas() + { + // Arrange + var routeValues = _areaTestContext; + var expectedSeeds = new[] + { + "/Areas/{2}/Views/{1}/{0}.cshtml", + "/Areas/{2}/Views/Shared/{0}.cshtml", + "/Views/Shared/{0}.cshtml" + }; + var page = Mock.Of(); var pageFactory = new Mock(); pageFactory diff --git a/src/Mvc/Mvc.Razor/test/TagHelpers/UrlResolutionTagHelperTest.cs b/src/Mvc/Mvc.Razor/test/TagHelpers/UrlResolutionTagHelperTest.cs index 0eab7b1331..aee23f4242 100644 --- a/src/Mvc/Mvc.Razor/test/TagHelpers/UrlResolutionTagHelperTest.cs +++ b/src/Mvc/Mvc.Razor/test/TagHelpers/UrlResolutionTagHelperTest.cs @@ -108,12 +108,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor.TagHelpers get { // url, expectedHref - return new TheoryData + return new TheoryData { - { new HtmlString("~/home/index.html"), "HtmlEncode[[/approot/]]home/index.html" }, - { new HtmlString(" ~/home/index.html"), "HtmlEncode[[/approot/]]home/index.html" }, + { "~/home/index.html", "HtmlEncode[[/approot/]]home/index.html" }, + { " ~/home/index.html", "HtmlEncode[[/approot/]]home/index.html" }, { - new HtmlString("~/home/index.html ~/secondValue/index.html"), + "~/home/index.html ~/secondValue/index.html", "HtmlEncode[[/approot/]]home/index.html ~/secondValue/index.html" }, }; @@ -122,14 +122,14 @@ namespace Microsoft.AspNetCore.Mvc.Razor.TagHelpers [Theory] [MemberData(nameof(ResolvableUrlHtmlStringData))] - public void Process_ResolvesTildeSlashValues_InHtmlString(object url, string expectedHref) + public void Process_ResolvesTildeSlashValues_InHtmlString(string url, string expectedHref) { // Arrange var tagHelperOutput = new TagHelperOutput( tagName: "a", attributes: new TagHelperAttributeList { - { "href", url } + { "href", new HtmlString(url) } }, getChildContentAsync: (useCachedResult, encoder) => Task.FromResult(null)); var urlHelperMock = new Mock(); @@ -221,27 +221,27 @@ namespace Microsoft.AspNetCore.Mvc.Razor.TagHelpers get { // url - return new TheoryData + return new TheoryData { - { new HtmlString("/home/index.html") }, - { new HtmlString("~ /home/index.html") }, - { new HtmlString("/home/index.html ~/second/wontresolve.html") }, - { new HtmlString("~\\home\\index.html") }, - { new HtmlString("~\\/home/index.html") }, + { "/home/index.html" }, + { "~ /home/index.html" }, + { "/home/index.html ~/second/wontresolve.html" }, + { "~\\home\\index.html" }, + { "~\\/home/index.html" }, }; } } [Theory] [MemberData(nameof(UnresolvableUrlHtmlStringData))] - public void Process_DoesNotResolveNonTildeSlashValues_InHtmlString(HtmlString url) + public void Process_DoesNotResolveNonTildeSlashValues_InHtmlString(string url) { // Arrange var tagHelperOutput = new TagHelperOutput( tagName: "a", attributes: new TagHelperAttributeList { - { "href", url } + { "href", new HtmlString(url) } }, getChildContentAsync: (useCachedResult, encoder) => Task.FromResult(null)); var urlHelperMock = new Mock();