Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
Adding PageActionDescriptorProvider
Browse files Browse the repository at this point in the history
Fixes #5353
  • Loading branch information
pranavkm committed Nov 21, 2016
1 parent 8d636bf commit 773e350
Show file tree
Hide file tree
Showing 3 changed files with 394 additions and 0 deletions.
@@ -0,0 +1,59 @@
// 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;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Razor.Evolution;
using Microsoft.Extensions.FileProviders;

namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
{
public class DefaultRazorProject : RazorProject
{
private const string RazorFileExtension = ".cshtml";
private readonly IFileProvider _provider;

public DefaultRazorProject(IFileProvider provider)
{
_provider = provider;
}

public override IEnumerable<RazorProjectItem> EnumerateItems(string path)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}

if (path.Length == 0 || path[0] != '/')
{
throw new ArgumentException(Resources.RazorProject_PathMustStartWithForwardSlash);
}

return EnumerateFiles(_provider.GetDirectoryContents(path), path, "");
}

private IEnumerable<RazorProjectItem> EnumerateFiles(IDirectoryContents directory, string basePath, string prefix)
{
if (directory.Exists)
{
foreach (var file in directory)
{
if (file.IsDirectory)
{
var children = EnumerateFiles(_provider.GetDirectoryContents(file.PhysicalPath), basePath, prefix + "/" + file.Name);
foreach (var child in children)
{
yield return child;
}
}
else if (string.Equals(RazorFileExtension, Path.GetExtension(file.Name), StringComparison.OrdinalIgnoreCase))
{
yield return new DefaultRazorProjectItem(file, basePath, prefix + "/" + file.Name);
}
}
}
}
}
}
@@ -0,0 +1,32 @@
// 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.IO;
using Microsoft.AspNetCore.Razor.Evolution;
using Microsoft.Extensions.FileProviders;

namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
{
public class DefaultRazorProjectItem : RazorProjectItem
{
private readonly IFileInfo _fileInfo;

public DefaultRazorProjectItem(IFileInfo fileInfo, string basePath, string path)
{
_fileInfo = fileInfo;
BasePath = basePath;
Path = path;
}

public override string BasePath { get; }

public override string Path { get; }

public override string PhysicalPath => _fileInfo.PhysicalPath;

public override Stream Read()
{
return _fileInfo.CreateReadStream();
}
}
}
@@ -0,0 +1,303 @@
// 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;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;
using Microsoft.AspNetCore.Razor.Evolution;

namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
{
public class PageActionDescriptorProviderTest
{
[Fact]
public void GetDescriptors_DoesNotAddDescriptorsForFilesWithoutDirectives()
{
// Arrange
var razorProject = new Mock<RazorProject>();
razorProject.Setup(p => p.EnumerateItems("/"))
.Returns(new[]
{
GetProjectItem("/", "/Index.cshtml", "<h1>Hello world</h1>"),
});
var provider = new PageActionDescriptorProvider(
razorProject.Object,
GetAccessor<MvcOptions>(),
GetAccessor<RazorPagesOptions>());
var context = new ActionDescriptorProviderContext();

// Act
provider.OnProvidersExecuting(context);

// Assert
Assert.Empty(context.Results);
}

[Fact]
public void GetDescriptors_AddsDescriptorsForFileWithPageDirective()
{
// Arrange
var razorProject = new Mock<RazorProject>();
razorProject.Setup(p => p.EnumerateItems("/"))
.Returns(new[]
{
GetProjectItem("/", "/Test.cshtml", $"@page{Environment.NewLine}<h1>Hello world</h1>"),
});
var provider = new PageActionDescriptorProvider(
razorProject.Object,
GetAccessor<MvcOptions>(),
GetAccessor<RazorPagesOptions>());
var context = new ActionDescriptorProviderContext();

// Act
provider.OnProvidersExecuting(context);

// Assert
var result = Assert.Single(context.Results);
var descriptor = Assert.IsType<PageActionDescriptor>(result);
Assert.Equal("/Test.cshtml", descriptor.RelativePath);
Assert.Equal("/Test", descriptor.RouteValues["page"]);
Assert.Equal("Test", descriptor.AttributeRouteInfo.Template);
}

[Fact]
public void GetDescriptors_AddsDescriptorsForFileWithPageDirectiveAndRouteTemplate()
{
// Arrange
var razorProject = new Mock<RazorProject>();
razorProject.Setup(p => p.EnumerateItems("/"))
.Returns(new[]
{
GetProjectItem("/", "/Test.cshtml", $"@page Home {Environment.NewLine}<h1>Hello world</h1>"),
});
var provider = new PageActionDescriptorProvider(
razorProject.Object,
GetAccessor<MvcOptions>(),
GetAccessor<RazorPagesOptions>());
var context = new ActionDescriptorProviderContext();

// Act
provider.OnProvidersExecuting(context);

// Assert
var result = Assert.Single(context.Results);
var descriptor = Assert.IsType<PageActionDescriptor>(result);
Assert.Equal("/Test.cshtml", descriptor.RelativePath);
Assert.Equal("/Test", descriptor.RouteValues["page"]);
Assert.Equal("Test/Home", descriptor.AttributeRouteInfo.Template);
}

[Theory]
[InlineData("/Path1")]
[InlineData("~/Path1")]
public void GetDescriptors_ThrowsIfRouteTemplatesAreOverriden(string template)
{
// Arrange
var razorProject = new Mock<RazorProject>();
razorProject.Setup(p => p.EnumerateItems("/"))
.Returns(new[]
{
GetProjectItem("/", "/Test.cshtml", $"@page {template} {Environment.NewLine}<h1>Hello world</h1>"),
});
var provider = new PageActionDescriptorProvider(
razorProject.Object,
GetAccessor<MvcOptions>(),
GetAccessor<RazorPagesOptions>());
var context = new ActionDescriptorProviderContext();

// Act and Assert
var ex = Assert.Throws<InvalidOperationException>(() => provider.OnProvidersExecuting(context));
Assert.Equal(
"The @page directive for the Razor page at /Test.cshtml cannot override the relative path prefix.",
ex.Message);
}

[Fact]
public void GetDescriptors_WithEmptyPageDirective_MapsIndexToEmptySegment()
{
// Arrange
var razorProject = new Mock<RazorProject>();
razorProject.Setup(p => p.EnumerateItems("/"))
.Returns(new[]
{
GetProjectItem("/About", "/Index.cshtml", $"@page {Environment.NewLine}"),
});
var provider = new PageActionDescriptorProvider(
razorProject.Object,
GetAccessor<MvcOptions>(),
GetAccessor<RazorPagesOptions>());
var context = new ActionDescriptorProviderContext();

// Act
provider.OnProvidersExecuting(context);

// Assert
Assert.Collection(context.Results,
result =>
{
var descriptor = Assert.IsType<PageActionDescriptor>(result);
Assert.Equal("/About/Index.cshtml", descriptor.RelativePath);
Assert.Equal("/Index", descriptor.RouteValues["page"]);
Assert.Equal("About/Index", descriptor.AttributeRouteInfo.Template);
},
result =>
{
var descriptor = Assert.IsType<PageActionDescriptor>(result);
Assert.Equal("/About/Index.cshtml", descriptor.RelativePath);
Assert.Equal("/Index", descriptor.RouteValues["page"]);
Assert.Equal("About", descriptor.AttributeRouteInfo.Template);
});
}

[Fact]
public void GetDescriptors_WithNonEmptyPageDirective_MapsIndexToEmptySegment()
{
// Arrange
var razorProject = new Mock<RazorProject>();
razorProject.Setup(p => p.EnumerateItems("/"))
.Returns(new[]
{
GetProjectItem("/Catalog/Details", "/Index.cshtml", $"@page {{id:int?}} {Environment.NewLine}"),
});
var provider = new PageActionDescriptorProvider(
razorProject.Object,
GetAccessor<MvcOptions>(),
GetAccessor<RazorPagesOptions>());
var context = new ActionDescriptorProviderContext();

// Act
provider.OnProvidersExecuting(context);

// Assert
Assert.Collection(context.Results,
result =>
{
var descriptor = Assert.IsType<PageActionDescriptor>(result);
Assert.Equal("/Catalog/Details/Index.cshtml", descriptor.RelativePath);
Assert.Equal("/Index", descriptor.RouteValues["page"]);
Assert.Equal("Catalog/Details/Index/{id:int?}", descriptor.AttributeRouteInfo.Template);
},
result =>
{
var descriptor = Assert.IsType<PageActionDescriptor>(result);
Assert.Equal("/Catalog/Details/Index.cshtml", descriptor.RelativePath);
Assert.Equal("/Index", descriptor.RouteValues["page"]);
Assert.Equal("Catalog/Details/{id:int?}", descriptor.AttributeRouteInfo.Template);
});
}

[Fact]
public void GetDescriptors_AddsGlobalFilters()
{
// Arrange
var filter1 = Mock.Of<IFilterMetadata>();
var filter2 = Mock.Of<IFilterMetadata>();
var options = new MvcOptions();
options.Filters.Add(filter1);
options.Filters.Add(filter2);
var razorProject = new Mock<RazorProject>();
razorProject.Setup(p => p.EnumerateItems("/"))
.Returns(new[]
{
GetProjectItem("/", "/Home.cshtml", $"@page {Environment.NewLine}"),
});
var provider = new PageActionDescriptorProvider(
razorProject.Object,
GetAccessor(options),
GetAccessor<RazorPagesOptions>());
var context = new ActionDescriptorProviderContext();

// Act
provider.OnProvidersExecuting(context);

// Assert
var result = Assert.Single(context.Results);
var descriptor = Assert.IsType<PageActionDescriptor>(result);
Assert.Collection(descriptor.FilterDescriptors,
filterDescriptor =>
{
Assert.Equal(FilterScope.Global, filterDescriptor.Scope);
Assert.Same(filter1, filterDescriptor.Filter);
},
filterDescriptor =>
{
Assert.Equal(FilterScope.Global, filterDescriptor.Scope);
Assert.Same(filter2, filterDescriptor.Filter);
});
}

[Fact]
public void GetDescriptors_AddsFiltersAddedByConvention()
{
// Arrange
var globalFilter = Mock.Of<IFilterMetadata>();
var localFilter = Mock.Of<IFilterMetadata>();
var options = new MvcOptions();
options.Filters.Add(globalFilter);
var convention = new Mock<IPageModelConvention>();
convention.Setup(c => c.Apply(It.IsAny<PageModel>()))
.Callback((PageModel model) =>
{
model.Filters.Add(localFilter);
});
var razorOptions = new RazorPagesOptions();
razorOptions.Conventions.Add(convention.Object);

var razorProject = new Mock<RazorProject>();
razorProject.Setup(p => p.EnumerateItems("/"))
.Returns(new[]
{
GetProjectItem("/", "/Home.cshtml", $"@page {Environment.NewLine}"),
});
var provider = new PageActionDescriptorProvider(
razorProject.Object,
GetAccessor(options),
GetAccessor(razorOptions));
var context = new ActionDescriptorProviderContext();

// Act
provider.OnProvidersExecuting(context);

// Assert
var result = Assert.Single(context.Results);
var descriptor = Assert.IsType<PageActionDescriptor>(result);
Assert.Collection(descriptor.FilterDescriptors,
filterDescriptor =>
{
Assert.Equal(FilterScope.Global, filterDescriptor.Scope);
Assert.Same(globalFilter, filterDescriptor.Filter);
},
filterDescriptor =>
{
Assert.Equal(FilterScope.Action, filterDescriptor.Scope);
Assert.Same(localFilter, filterDescriptor.Filter);
});
}

private static IOptions<TOptions> GetAccessor<TOptions>(TOptions options = null)
where TOptions : class, new()
{
var accessor = new Mock<IOptions<TOptions>>();
accessor.SetupGet(a => a.Value).Returns(options ?? new TOptions());
return accessor.Object;
}

private static RazorProjectItem GetProjectItem(string basePath, string path, string content)
{
var testFileInfo = new TestFileInfo
{
Content = content,
};

return new DefaultRazorProjectItem(testFileInfo, basePath, path);
}


}
}

0 comments on commit 773e350

Please sign in to comment.