// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using System.Web.Caching; using Moq; using Xunit; using Assert = Microsoft.TestCommon.AssertEx; namespace System.Web.WebPages.Test { public class WebPageTest { private const string XmlHttpRequestKey = "X-Requested-With"; private const string XmlHttpRequestValue = "XMLHttpRequest"; [Fact] public void CreatePageFromVirtualPathAssignsVirtualPathFactory() { // Arrange var path = "~/index.cshtml"; var page = Utils.CreatePage(null, path); var factory = new HashVirtualPathFactory(page); // Act var result = WebPage.CreateInstanceFromVirtualPath(path, factory); // Assert Assert.Equal(page, result); Assert.Equal(page.VirtualPathFactory, factory); Assert.Equal(page.VirtualPath, path); } [Fact] public void NormalizeLayoutPagePathTest() { var layoutPage = "Layout.cshtml"; var layoutPath1 = "~/MyApp/Layout.cshtml"; var page = new Mock() { CallBase = true }.Object; page.VirtualPath = "~/MyApp/index.cshtml"; var mockBuildManager = new Mock(); mockBuildManager.Setup(c => c.Exists(It.IsAny())).Returns(p => p.Equals(layoutPath1, StringComparison.OrdinalIgnoreCase)); page.VirtualPathFactory = mockBuildManager.Object; Assert.Equal(layoutPath1, page.NormalizeLayoutPagePath(layoutPage)); mockBuildManager.Setup(c => c.Exists(It.IsAny())).Returns(_ => false); Assert.Throws(() => page.NormalizeLayoutPagePath(layoutPage), @"The layout page ""Layout.cshtml"" could not be found at the following path: ""~/MyApp/Layout.cshtml""."); } [Fact] public void UrlDataBasicTests() { Mock mockContext = new Mock(); mockContext.Setup(context => context.Items).Returns(new Hashtable()); mockContext.Object.Items[typeof(WebPageMatch)] = new WebPageMatch("~/a.cshtml", "one/2/3.0/4.0005"); WebPage page = new Mock() { CallBase = true }.Object; page.Context = mockContext.Object; Assert.Equal("one", page.UrlData[0]); Assert.Equal(2, page.UrlData[1].AsInt()); Assert.Equal(3.0f, page.UrlData[2].AsFloat()); Assert.Equal(4.0005m, page.UrlData[3].AsDecimal()); } [Fact] public void UrlDataEmptyTests() { Mock mockContext = new Mock(); mockContext.Setup(context => context.Items).Returns(new Hashtable()); mockContext.Object.Items[typeof(WebPageMatch)] = new WebPageMatch("~/a.cshtml", "one///two/"); WebPage page = new Mock() { CallBase = true }.Object; page.Context = mockContext.Object; Assert.Equal("one", page.UrlData[0]); Assert.True(page.UrlData[1].IsEmpty()); Assert.True(page.UrlData[2].IsEmpty()); Assert.Equal("two", page.UrlData[3]); Assert.True(page.UrlData[4].IsEmpty()); } [Fact] public void UrlDataReadOnlyTest() { Mock mockContext = new Mock(); mockContext.Setup(context => context.Items).Returns(new Hashtable()); mockContext.Object.Items[typeof(WebPageMatch)] = new WebPageMatch("~/a.cshtml", "one/2/3.0/4.0005"); WebPage page = new Mock() { CallBase = true }.Object; page.Context = mockContext.Object; Assert.Throws(() => { page.UrlData.Add("bogus"); }, "The UrlData collection is read-only."); Assert.Throws(() => { page.UrlData.Insert(0, "bogus"); }, "The UrlData collection is read-only."); Assert.Throws(() => { page.UrlData.Remove("one"); }, "The UrlData collection is read-only."); } [Fact] public void UrlDataOutOfBoundsTest() { Mock mockContext = new Mock(); mockContext.Setup(context => context.Items).Returns(new Hashtable()); mockContext.Object.Items[typeof(WebPageMatch)] = new WebPageMatch("~/a.cshtml", ""); WebPage page = new Mock() { CallBase = true }.Object; page.Context = mockContext.Object; Assert.Equal(String.Empty, page.UrlData[0]); Assert.Equal(String.Empty, page.UrlData[1]); } [Fact] public void NullModelTest() { var page = CreateMockPageWithPostContext().Object; page.PageContext.Model = null; Assert.Null(page.Model); } internal class ModelTestClass { public string Prop1 { get; set; } public string GetProp1() { return Prop1; } public override string ToString() { return Prop1; } } [Fact] public void ModelTest() { var v = "value1"; var page = CreateMockPageWithPostContext().Object; var model = new ModelTestClass() { Prop1 = v }; page.PageContext.Model = model; Assert.NotNull(page.Model); Assert.Equal(v, page.Model.Prop1); Assert.Equal(v, page.Model.GetProp1()); Assert.Equal(v, page.Model.ToString()); Assert.Equal(model, (ModelTestClass)page.Model); // No such property Assert.Null(page.Model.Prop2); // No such method Assert.Throws(() => page.Model.DoSomething()); } [Fact] public void AnonymousObjectModelTest() { var v = "value1"; var page = CreateMockPageWithPostContext().Object; var model = new { Prop1 = v }; page.PageContext.Model = model; Assert.NotNull(page.Model); Assert.Equal(v, page.Model.Prop1); // No such property Assert.Null(page.Model.Prop2); // No such method Assert.Throws(() => page.Model.DoSomething()); } [Fact] public void SessionPropertyTest() { var page = CreateMockPageWithPostContext().Object; Assert.Equal(0, page.Session.Count); } [Fact] public void AppStatePropertyTest() { var page = CreateMockPageWithPostContext().Object; Assert.Equal(0, page.AppState.Count); } [Fact] public void ExecutePageHierarchyTest() { var page = new Mock(); page.Object.TopLevelPage = true; var executors = new List(); // First executor returns false var executor1 = new Mock(); executor1.Setup(exec => exec.Execute(It.IsAny())).Returns(false); executors.Add(executor1.Object); // Second executor returns true var executor2 = new Mock(); executor2.Setup(exec => exec.Execute(It.IsAny())).Returns(true); executors.Add(executor2.Object); // Third executor should never get called, since we stop after the first true var executor3 = new Mock(); executor3.Setup(exec => exec.Execute(It.IsAny())).Returns(false); executors.Add(executor3.Object); page.Object.ExecutePageHierarchy(executors); // Make sure the first two got called but not the third executor1.Verify(exec => exec.Execute(It.IsAny())); executor2.Verify(exec => exec.Execute(It.IsAny())); executor3.Verify(exec => exec.Execute(It.IsAny()), Times.Never()); } [Fact] public void IsPostReturnsTrueWhenMethodIsPost() { // Arrange var page = CreateMockPageWithPostContext(); // Act and Assert Assert.True(page.Object.IsPost); } [Fact] public void IsPostReturnsFalseWhenMethodIsNotPost() { // Arrange var methods = new[] { "GET", "DELETE", "PUT", "RANDOM" }; // Act and Assert Assert.True(methods.All(method => !CreateMockPageWithContext(method).Object.IsPost)); } [Fact] public void IsAjaxReturnsTrueWhenRequestContainsAjaxHeader() { // Arrange var headers = new NameValueCollection(); headers.Add("X-Requested-With", "XMLHttpRequest"); var context = CreateContext("GET", new NameValueCollection(), headers); var page = CreatePage(context); // Act and Assert Assert.True(page.Object.IsAjax); } [Fact] public void IsAjaxReturnsTrueWhenRequestBodyContainsAjaxHeader() { // Arrange var headers = new NameValueCollection(); headers.Add("X-Requested-With", "XMLHttpRequest"); var context = CreateContext("POST", headers, headers); var page = CreatePage(context); // Act and Assert Assert.True(page.Object.IsAjax); } [Fact] public void IsAjaxReturnsFalseWhenRequestDoesNotContainAjaxHeaders() { // Arrange var page = CreateMockPageWithPostContext(); // Act and Assert Assert.True(!page.Object.IsAjax); } private static Mock CreatePage(Mock context) { var page = new Mock() { CallBase = true }; var pageContext = new WebPageContext(); page.Object.Context = context.Object; page.Object.PageContext = pageContext; return page; } private static Mock CreateMockPageWithPostContext() { return CreateMockPageWithContext("POST"); } private static Mock CreateMockPageWithContext(string httpMethod) { var context = CreateContext(httpMethod, new NameValueCollection()); var page = CreatePage(context); return page; } private static Mock CreateContext(string httpMethod, NameValueCollection queryString, NameValueCollection httpHeaders = null) { var request = new Mock(); request.Setup(r => r.HttpMethod).Returns(httpMethod); request.Setup(r => r.QueryString).Returns(queryString); request.Setup(r => r.Form).Returns(new NameValueCollection()); request.Setup(r => r.Files).Returns(new Mock().Object); request.Setup(c => c.Headers).Returns(httpHeaders); var context = new Mock(); context.Setup(c => c.Response).Returns(new Mock().Object); context.Setup(c => c.Request).Returns(request.Object); context.Setup(c => c.Items).Returns(new Hashtable()); context.Setup(c => c.Session).Returns(new Mock().Object); context.Setup(c => c.Application).Returns(new Mock().Object); context.Setup(c => c.Cache).Returns(new Cache()); context.Setup(c => c.Server).Returns(new Mock().Object); return context; } } }