1 // Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
2 
3 using System.IO;
4 using System.Web.Mvc.Properties;
5 using System.Web.Routing;
6 using Moq;
7 using Xunit;
8 using Assert = Microsoft.TestCommon.AssertEx;
9 
10 namespace System.Web.Mvc.Html.Test
11 {
12     public class ChildActionExtensionsTest
13     {
14         Mock<HtmlHelper> htmlHelper;
15         Mock<HttpContextBase> httpContext;
16         Mock<RouteBase> route;
17         Mock<IViewDataContainer> viewDataContainer;
18 
19         RouteData originalRouteData;
20         RouteCollection routes;
21         ViewContext viewContext;
22         VirtualPathData virtualPathData;
23 
ChildActionExtensionsTest()24         public ChildActionExtensionsTest()
25         {
26             route = new Mock<RouteBase>();
27             route.Setup(r => r.GetVirtualPath(It.IsAny<RequestContext>(), It.IsAny<RouteValueDictionary>()))
28                 .Returns(() => virtualPathData);
29 
30             virtualPathData = new VirtualPathData(route.Object, "~/VirtualPath");
31 
32             routes = new RouteCollection();
33             routes.Add(route.Object);
34 
35             originalRouteData = new RouteData();
36 
37             string returnValue = "";
38             httpContext = new Mock<HttpContextBase>();
39             httpContext.Setup(hc => hc.Request.ApplicationPath).Returns("~");
40             httpContext.Setup(hc => hc.Response.ApplyAppPathModifier(It.IsAny<string>()))
41                 .Callback<string>(s => returnValue = s)
42                 .Returns(() => returnValue);
43             httpContext.Setup(hc => hc.Server.Execute(It.IsAny<IHttpHandler>(), It.IsAny<TextWriter>(), It.IsAny<bool>()));
44 
45             viewContext = new ViewContext
46             {
47                 RequestContext = new RequestContext(httpContext.Object, originalRouteData)
48             };
49 
50             viewDataContainer = new Mock<IViewDataContainer>();
51 
52             htmlHelper = new Mock<HtmlHelper>(viewContext, viewDataContainer.Object, routes);
53         }
54 
55         [Fact]
GuardClauses()56         public void GuardClauses()
57         {
58             // Act & Assert
59             Assert.ThrowsArgumentNull(
60                 () => ChildActionExtensions.ActionHelper(null /* htmlHelper */, "abc", null /* controllerName */, null /* routeValues */, null /* textWriter */),
61                 "htmlHelper"
62                 );
63 
64             Assert.ThrowsArgumentNullOrEmpty(
65                 () => ChildActionExtensions.ActionHelper(htmlHelper.Object, null /* actionName */, null /* controllerName */, null /* routeValues */, null /* textWriter */),
66                 "actionName"
67                 );
68 
69             Assert.ThrowsArgumentNullOrEmpty(
70                 () => ChildActionExtensions.ActionHelper(htmlHelper.Object, String.Empty /* actionName */, null /* controllerName */, null /* routeValues */, null /* textWriter */),
71                 "actionName"
72                 );
73         }
74 
75         [Fact]
ServerExecuteCalledWithWrappedChildActionMvcHandler()76         public void ServerExecuteCalledWithWrappedChildActionMvcHandler()
77         {
78             // Arrange
79             IHttpHandler callbackHandler = null;
80             TextWriter callbackTextWriter = null;
81             bool callbackPreserveForm = false;
82             httpContext.Setup(hc => hc.Server.Execute(It.IsAny<IHttpHandler>(), It.IsAny<TextWriter>(), It.IsAny<bool>()))
83                 .Callback<IHttpHandler, TextWriter, bool>(
84                     (handler, textWriter, preserveForm) =>
85                     {
86                         callbackHandler = handler;
87                         callbackTextWriter = textWriter;
88                         callbackPreserveForm = preserveForm;
89                     });
90             TextWriter stringWriter = new StringWriter();
91 
92             // Act
93             ChildActionExtensions.ActionHelper(htmlHelper.Object, "actionName", null /* controllerName */, null /* routeValues */, stringWriter);
94 
95             // Assert
96             Assert.NotNull(callbackHandler);
97             HttpHandlerUtil.ServerExecuteHttpHandlerWrapper wrapper = callbackHandler as HttpHandlerUtil.ServerExecuteHttpHandlerWrapper;
98             Assert.NotNull(wrapper);
99             Assert.NotNull(wrapper.InnerHandler);
100             ChildActionExtensions.ChildActionMvcHandler childHandler = wrapper.InnerHandler as ChildActionExtensions.ChildActionMvcHandler;
101             Assert.NotNull(childHandler);
102             Assert.Same(stringWriter, callbackTextWriter);
103             Assert.True(callbackPreserveForm);
104         }
105 
106         [Fact]
RouteDataTokensIncludesParentActionViewContext()107         public void RouteDataTokensIncludesParentActionViewContext()
108         {
109             // Arrange
110             MvcHandler mvcHandler = null;
111             httpContext.Setup(hc => hc.Server.Execute(It.IsAny<IHttpHandler>(), It.IsAny<TextWriter>(), It.IsAny<bool>()))
112                 .Callback<IHttpHandler, TextWriter, bool>((handler, _, __) => mvcHandler = (MvcHandler)((HttpHandlerUtil.ServerExecuteHttpHandlerWrapper)handler).InnerHandler);
113 
114             // Act
115             ChildActionExtensions.ActionHelper(htmlHelper.Object, "actionName", null /* controllerName */, null /* routeValues */, null /* textWriter */);
116 
117             // Assert
118             Assert.Same(viewContext, mvcHandler.RequestContext.RouteData.DataTokens[ControllerContext.ParentActionViewContextToken]);
119         }
120 
121         [Fact]
RouteValuesIncludeNewActionName()122         public void RouteValuesIncludeNewActionName()
123         {
124             // Arrange
125             MvcHandler mvcHandler = null;
126             httpContext.Setup(hc => hc.Server.Execute(It.IsAny<IHttpHandler>(), It.IsAny<TextWriter>(), It.IsAny<bool>()))
127                 .Callback<IHttpHandler, TextWriter, bool>((handler, _, __) => mvcHandler = (MvcHandler)((HttpHandlerUtil.ServerExecuteHttpHandlerWrapper)handler).InnerHandler);
128 
129             // Act
130             ChildActionExtensions.ActionHelper(htmlHelper.Object, "actionName", null /* controllerName */, null /* routeValues */, null /* textWriter */);
131 
132             // Assert
133             RouteData routeData = mvcHandler.RequestContext.RouteData;
134             Assert.Equal("actionName", routeData.Values["action"]);
135         }
136 
137         [Fact]
RouteValuesIncludeOldControllerNameWhenControllerNameIsNullOrEmpty()138         public void RouteValuesIncludeOldControllerNameWhenControllerNameIsNullOrEmpty()
139         {
140             // Arrange
141             originalRouteData.Values["controller"] = "oldController";
142             MvcHandler mvcHandler = null;
143             httpContext.Setup(hc => hc.Server.Execute(It.IsAny<IHttpHandler>(), It.IsAny<TextWriter>(), It.IsAny<bool>()))
144                 .Callback<IHttpHandler, TextWriter, bool>((handler, _, __) => mvcHandler = (MvcHandler)((HttpHandlerUtil.ServerExecuteHttpHandlerWrapper)handler).InnerHandler);
145 
146             // Act
147             ChildActionExtensions.ActionHelper(htmlHelper.Object, "actionName", null /* controllerName */, null /* routeValues */, null /* textWriter */);
148 
149             // Assert
150             RouteData routeData = mvcHandler.RequestContext.RouteData;
151             Assert.Equal("oldController", routeData.Values["controller"]);
152         }
153 
154         [Fact]
RouteValuesIncludeNewControllerNameWhenControllNameIsNotEmpty()155         public void RouteValuesIncludeNewControllerNameWhenControllNameIsNotEmpty()
156         {
157             // Arrange
158             originalRouteData.Values["controller"] = "oldController";
159             MvcHandler mvcHandler = null;
160             httpContext.Setup(hc => hc.Server.Execute(It.IsAny<IHttpHandler>(), It.IsAny<TextWriter>(), It.IsAny<bool>()))
161                 .Callback<IHttpHandler, TextWriter, bool>((handler, _, __) => mvcHandler = (MvcHandler)((HttpHandlerUtil.ServerExecuteHttpHandlerWrapper)handler).InnerHandler);
162 
163             // Act
164             ChildActionExtensions.ActionHelper(htmlHelper.Object, "actionName", "newController", null /* routeValues */, null /* textWriter */);
165 
166             // Assert
167             RouteData routeData = mvcHandler.RequestContext.RouteData;
168             Assert.Equal("newController", routeData.Values["controller"]);
169         }
170 
171         [Fact]
PassedRouteValuesOverrideParentRequestRouteValues()172         public void PassedRouteValuesOverrideParentRequestRouteValues()
173         {
174             // Arrange
175             originalRouteData.Values["name1"] = "value1";
176             originalRouteData.Values["name2"] = "value2";
177             MvcHandler mvcHandler = null;
178             httpContext.Setup(hc => hc.Server.Execute(It.IsAny<IHttpHandler>(), It.IsAny<TextWriter>(), It.IsAny<bool>()))
179                 .Callback<IHttpHandler, TextWriter, bool>((handler, _, __) => mvcHandler = (MvcHandler)((HttpHandlerUtil.ServerExecuteHttpHandlerWrapper)handler).InnerHandler);
180 
181             // Act
182             ChildActionExtensions.ActionHelper(htmlHelper.Object, "actionName", null /* controllerName */, new RouteValueDictionary { { "name2", "newValue2" } }, null /* textWriter */);
183 
184             // Assert
185             RouteData routeData = mvcHandler.RequestContext.RouteData;
186             Assert.Equal("value1", routeData.Values["name1"]);
187             Assert.Equal("newValue2", routeData.Values["name2"]);
188 
189             Assert.Equal("newValue2", (routeData.Values[ChildActionValueProvider.ChildActionValuesKey] as DictionaryValueProvider<object>).GetValue("name2").RawValue);
190         }
191 
192         [Fact]
NoChildActionValuesDictionaryCreatedIfNoRouteValuesPassed()193         public void NoChildActionValuesDictionaryCreatedIfNoRouteValuesPassed()
194         {
195             // Arrange
196             MvcHandler mvcHandler = null;
197             httpContext.Setup(hc => hc.Server.Execute(It.IsAny<IHttpHandler>(), It.IsAny<TextWriter>(), It.IsAny<bool>()))
198                 .Callback<IHttpHandler, TextWriter, bool>((handler, _, __) => mvcHandler = (MvcHandler)((HttpHandlerUtil.ServerExecuteHttpHandlerWrapper)handler).InnerHandler);
199 
200             // Act
201             ChildActionExtensions.ActionHelper(htmlHelper.Object, "actionName", null /* controllerName */, null, null /* textWriter */);
202 
203             // Assert
204             RouteData routeData = mvcHandler.RequestContext.RouteData;
205             Assert.Null(routeData.Values[ChildActionValueProvider.ChildActionValuesKey]);
206         }
207 
208         [Fact]
RouteValuesDoesNotIncludeExplicitlyPassedAreaName()209         public void RouteValuesDoesNotIncludeExplicitlyPassedAreaName()
210         {
211             // Arrange
212             Route route = routes.MapRoute("my-area", "my-area");
213             route.DataTokens["area"] = "myArea";
214             MvcHandler mvcHandler = null;
215             httpContext.Setup(hc => hc.Server.Execute(It.IsAny<IHttpHandler>(), It.IsAny<TextWriter>(), It.IsAny<bool>()))
216                 .Callback<IHttpHandler, TextWriter, bool>((handler, _, __) => mvcHandler = (MvcHandler)((HttpHandlerUtil.ServerExecuteHttpHandlerWrapper)handler).InnerHandler);
217 
218             // Act
219             ChildActionExtensions.ActionHelper(htmlHelper.Object, "actionName", null /* controllerName */, new RouteValueDictionary { { "area", "myArea" } }, null /* textWriter */);
220 
221             // Assert
222             RouteData routeData = mvcHandler.RequestContext.RouteData;
223             Assert.False(routeData.Values.ContainsKey("area"));
224             Assert.Null((routeData.Values[ChildActionValueProvider.ChildActionValuesKey] as DictionaryValueProvider<object>).GetValue("area"));
225         }
226 
227         [Fact]
RouteValuesIncludeExplicitlyPassedAreaNameIfAreasNotInUse()228         public void RouteValuesIncludeExplicitlyPassedAreaNameIfAreasNotInUse()
229         {
230             // Arrange
231             Route route = routes.MapRoute("my-area", "my-area");
232             MvcHandler mvcHandler = null;
233             httpContext.Setup(hc => hc.Server.Execute(It.IsAny<IHttpHandler>(), It.IsAny<TextWriter>(), It.IsAny<bool>()))
234                 .Callback<IHttpHandler, TextWriter, bool>((handler, _, __) => mvcHandler = (MvcHandler)((HttpHandlerUtil.ServerExecuteHttpHandlerWrapper)handler).InnerHandler);
235 
236             // Act
237             ChildActionExtensions.ActionHelper(htmlHelper.Object, "actionName", null /* controllerName */, new RouteValueDictionary { { "area", "myArea" } }, null /* textWriter */);
238 
239             // Assert
240             RouteData routeData = mvcHandler.RequestContext.RouteData;
241             Assert.True(routeData.Values.ContainsKey("area"));
242             Assert.Equal("myArea", (routeData.Values[ChildActionValueProvider.ChildActionValuesKey] as DictionaryValueProvider<object>).GetValue("area").RawValue);
243         }
244 
245         [Fact]
NoMatchingRouteThrows()246         public void NoMatchingRouteThrows()
247         {
248             // Arrange
249             routes.Clear();
250 
251             // Act & Assert
252             Assert.Throws<InvalidOperationException>(
253                 () => ChildActionExtensions.ActionHelper(htmlHelper.Object, "actionName", null /* controllerName */, null /* routeValues */, null /* textWriter */),
254                 MvcResources.Common_NoRouteMatched
255                 );
256         }
257     }
258 }
259