diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/RazorProject.cs b/src/Microsoft.AspNetCore.Razor.Evolution/RazorProject.cs new file mode 100644 index 0000000000..98d3a386f7 --- /dev/null +++ b/src/Microsoft.AspNetCore.Razor.Evolution/RazorProject.cs @@ -0,0 +1,20 @@ +// 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; + +namespace Microsoft.AspNetCore.Razor.Evolution +{ + /// + /// An abstraction for working with a project containing Razor files. + /// + public abstract class RazorProject + { + /// + /// Gets a sequence of under the specific path in the project. + /// + /// The base path. + /// The sequence of . + public abstract IEnumerable EnumerateItems(string basePath); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/RazorProjectItem.cs b/src/Microsoft.AspNetCore.Razor.Evolution/RazorProjectItem.cs new file mode 100644 index 0000000000..108bc8c498 --- /dev/null +++ b/src/Microsoft.AspNetCore.Razor.Evolution/RazorProjectItem.cs @@ -0,0 +1,104 @@ +// 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.Diagnostics; +using System.IO; + +namespace Microsoft.AspNetCore.Razor.Evolution +{ + /// + /// An item in . + /// + [DebuggerDisplay("{CombinedPath}")] + public abstract class RazorProjectItem + { + /// + /// Path specified in . + /// + public abstract string BasePath { get; } + + /// + /// Path relative to . + /// + public abstract string Path { get; } + + /// + /// The absolute path to the file, including the file name. + /// + public abstract string PhysicalPath { get; } + + /// + /// Gets the file contents as readonly . + /// + /// The . + public abstract Stream Read(); + + /// + /// The root relative path of the item. + /// + public virtual string CombinedPath + { + get + { + if (BasePath == "/") + { + return Path; + } + else + { + return BasePath + Path; + } + } + } + + /// + /// The extension of the file. + /// + public virtual string Extension + { + get + { + var index = Filename.LastIndexOf('.'); + if (index == -1) + { + return null; + } + else + { + return Filename.Substring(index); + } + } + } + + /// + /// The name of the file including the extension. + /// + public virtual string Filename + { + get + { + var index = Path.LastIndexOf('/'); + return Path.Substring(index + 1); + } + } + + /// + /// Path relative to without the extension. + /// + public virtual string PathWithoutExtension + { + get + { + var index = Path.LastIndexOf('.'); + if (index == -1) + { + return Path; + } + else + { + return Path.Substring(0, index); + } + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/RazorProjectItemTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/RazorProjectItemTest.cs new file mode 100644 index 0000000000..26b830968a --- /dev/null +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/RazorProjectItemTest.cs @@ -0,0 +1,133 @@ +// 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.IO; +using Xunit; + +namespace Microsoft.AspNetCore.Razor.Evolution +{ + public class RazorProjectItemTest + { + [Fact] + public void CombinedPath_ReturnsPathIfBasePathIsEmpty() + { + // Arrange + var emptyBasePath = "/"; + var path = "/foo/bar.cshtml"; + var projectItem = new TestRazorProjectItem(emptyBasePath, path); + + // Act + var combinedPath = projectItem.CombinedPath; + + // Assert + Assert.Equal(path, combinedPath); + } + + [Theory] + [InlineData("/root", "/root/foo/bar.cshtml")] + [InlineData("root/subdir", "root/subdir/foo/bar.cshtml")] + public void CombinedPath_ConcatsPaths(string basePath, string expected) + { + // Arrange + var path = "/foo/bar.cshtml"; + var projectItem = new TestRazorProjectItem(basePath, path); + + // Act + var combinedPath = projectItem.CombinedPath; + + // Assert + Assert.Equal(expected, combinedPath); + } + + [Theory] + [InlineData("/Home/Index")] + [InlineData("EditUser")] + public void Extension_ReturnsNullIfFileDoesNotHaveExtension(string path) + { + // Arrange + var projectItem = new TestRazorProjectItem("/views", path); + + // Act + var extension = projectItem.Extension; + + // Assert + Assert.Null(extension); + } + + [Theory] + [InlineData("/Home/Index.cshtml", ".cshtml")] + [InlineData("/Home/Index.en-gb.cshtml", ".cshtml")] + [InlineData("EditUser.razor", ".razor")] + public void Extension_ReturnsFileExtension(string path, string expected) + { + // Arrange + var projectItem = new TestRazorProjectItem("/views", path); + + // Act + var extension = projectItem.Extension; + + // Assert + Assert.Equal(expected, extension); + } + + [Theory] + [InlineData("Home/Index.cshtml", "Index.cshtml")] + [InlineData("/Accounts/Customers/Manage-en-us.razor", "Manage-en-us.razor")] + public void FileName_ReturnsFileNameWithExtension(string path, string expected) + { + // Arrange + var projectItem = new TestRazorProjectItem("/", path); + + // Act + var fileName = projectItem.Filename; + + // Assert + Assert.Equal(expected, fileName); + } + + [Theory] + [InlineData("Home/Index", "Home/Index")] + [InlineData("Home/Index.cshtml", "Home/Index")] + [InlineData("/Accounts/Customers/Manage.en-us.razor", "/Accounts/Customers/Manage.en-us")] + [InlineData("/Accounts/Customers/Manage-en-us.razor", "/Accounts/Customers/Manage-en-us")] + public void PathWithoutExtension_ExcludesExtension(string path, string expected) + { + // Arrange + var projectItem = new TestRazorProjectItem("/", path); + + // Act + var fileName = projectItem.PathWithoutExtension; + + // Assert + Assert.Equal(expected, fileName); + } + + + private class TestRazorProjectItem : RazorProjectItem + { + public TestRazorProjectItem(string basePath, string path) + { + BasePath = basePath; + Path = path; + } + + public override string BasePath { get; } + + public override string Path { get; } + + public override string PhysicalPath + { + get + { + throw new NotImplementedException(); + } + } + + public override Stream Read() + { + throw new NotImplementedException(); + } + } + } +}