Use VirtualRazorProjectSystem for Razor tests

This commit is contained in:
Pranav K 2018-04-09 14:24:43 -07:00
parent 39053a5e22
commit 3db924003e
11 changed files with 400 additions and 258 deletions

View File

@ -28,6 +28,7 @@
<MicrosoftAspNetCoreMvcRazorExtensionsPackageVersion>2.1.0-preview3-32110</MicrosoftAspNetCoreMvcRazorExtensionsPackageVersion>
<MicrosoftAspNetCoreRangeHelperSourcesPackageVersion>2.1.0-preview3-32110</MicrosoftAspNetCoreRangeHelperSourcesPackageVersion>
<MicrosoftAspNetCoreRazorDesignPackageVersion>2.1.0-preview3-32110</MicrosoftAspNetCoreRazorDesignPackageVersion>
<MicrosoftAspNetCoreRazorLanguagePackageVersion>2.1.0-preview3-32110</MicrosoftAspNetCoreRazorLanguagePackageVersion>
<MicrosoftAspNetCoreRazorRuntimePackageVersion>2.1.0-preview3-32110</MicrosoftAspNetCoreRazorRuntimePackageVersion>
<MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>2.1.0-preview3-32110</MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>
<MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>2.1.0-preview3-32110</MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>

View File

@ -1,10 +1,8 @@
// 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 Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Razor.Hosting;
using Microsoft.AspNetCore.Razor.Language;
using Moq;
using Xunit;
using static Microsoft.AspNetCore.Razor.Hosting.TestRazorCompiledItem;
@ -14,15 +12,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
{
public ChecksumValidatorTest()
{
FileProvider = new TestFileProvider();
FileSystem = new FileProviderRazorProjectFileSystem(
Mock.Of<IRazorViewEngineFileProviderAccessor>(a => a.FileProvider == FileProvider),
Mock.Of<IHostingEnvironment>(e => e.ContentRootPath == "BasePath"));
ProjectFileSystem = new VirtualRazorProjectFileSystem();
}
public RazorProjectFileSystem FileSystem { get; }
public TestFileProvider FileProvider { get; }
public VirtualRazorProjectFileSystem ProjectFileSystem { get; }
[Fact]
public void IsRecompilationSupported_NoChecksums_ReturnsFalse()
@ -77,7 +70,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
var item = new TestRazorCompiledItem(typeof(string), "mvc.1.0.view", "/Views/Home/Index.cstml", new object[] { });
// Act
var result = ChecksumValidator.IsItemValid(FileSystem, item);
var result = ChecksumValidator.IsItemValid(ProjectFileSystem, item);
// Assert
Assert.True(result);
@ -94,7 +87,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
});
// Act
var result = ChecksumValidator.IsItemValid(FileSystem, item);
var result = ChecksumValidator.IsItemValid(ProjectFileSystem, item);
// Assert
Assert.True(result);
@ -110,10 +103,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), "/Views/Home/Index.cstml"),
});
FileProvider.AddFile("/Views/Home/_ViewImports.cstml", "dkdkfkdf"); // This will be ignored
ProjectFileSystem.Add(new TestRazorProjectItem("/Views/Home/_ViewImports.cstml", "dkdkfkdf")); // This will be ignored
// Act
var result = ChecksumValidator.IsItemValid(FileSystem, item);
var result = ChecksumValidator.IsItemValid(ProjectFileSystem, item);
// Assert
Assert.True(result);
@ -129,10 +122,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), "/Views/Home/Index.cstml"),
});
FileProvider.AddFile("/Views/Home/Index.cstml", "other content");
ProjectFileSystem.Add(new TestRazorProjectItem("/Views/Home/Index.cstml", "other content"));
// Act
var result = ChecksumValidator.IsItemValid(FileSystem, item);
var result = ChecksumValidator.IsItemValid(ProjectFileSystem, item);
// Assert
Assert.False(result);
@ -148,10 +141,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), "/Views/Home/Index.cstml"),
});
FileProvider.AddFile("/Views/Home/Index.cstml", "some content");
ProjectFileSystem.Add(new TestRazorProjectItem("/Views/Home/Index.cstml", "some content"));
// Act
var result = ChecksumValidator.IsItemValid(FileSystem, item);
var result = ChecksumValidator.IsItemValid(ProjectFileSystem, item);
// Assert
Assert.False(result);
@ -167,11 +160,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), "/Views/Home/Index.cstml"),
});
FileProvider.AddFile("/Views/Home/Index.cstml", "some content");
FileProvider.AddFile("/Views/Home/_ViewImports.cstml", "some other import");
ProjectFileSystem.Add(new TestRazorProjectItem("/Views/Home/Index.cstml", "some content"));
ProjectFileSystem.Add(new TestRazorProjectItem("/Views/Home/_ViewImports.cstml", "some other import"));
// Act
var result = ChecksumValidator.IsItemValid(FileSystem, item);
var result = ChecksumValidator.IsItemValid(ProjectFileSystem, item);
// Assert
Assert.False(result);
@ -188,12 +181,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), "/Views/Home/Index.cstml"),
});
FileProvider.AddFile("/Views/Home/Index.cstml", "some content");
FileProvider.AddFile("/Views/Home/_ViewImports.cstml", "some import");
FileProvider.AddFile("/Views/_ViewImports.cstml", "some other import");
ProjectFileSystem.Add(new TestRazorProjectItem("/Views/Home/Index.cstml", "some content"));
ProjectFileSystem.Add(new TestRazorProjectItem("/Views/Home/_ViewImports.cstml", "some import"));
ProjectFileSystem.Add(new TestRazorProjectItem("/Views/_ViewImports.cstml", "some other import"));
// Act
var result = ChecksumValidator.IsItemValid(FileSystem, item);
var result = ChecksumValidator.IsItemValid(ProjectFileSystem, item);
// Assert
Assert.True(result);

View File

@ -3,20 +3,16 @@
using System.IO;
using System.Text;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using Moq;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.Razor.Internal
{
public class CompilerFailedExceptionFactoryTest
{
private readonly IHostingEnvironment _hostingEnvironment = Mock.Of<IHostingEnvironment>(e => e.ContentRootPath == "BasePath");
[Fact]
public void GetCompilationFailedResult_ReadsRazorErrorsFromPage()
{
@ -24,11 +20,8 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
var viewPath = "/Views/Home/Index.cshtml";
var razorEngine = RazorEngine.Create();
var fileProvider = new TestFileProvider();
fileProvider.AddFile(viewPath, "<span name=\"@(User.Id\">");
var accessor = Mock.Of<IRazorViewEngineFileProviderAccessor>(a => a.FileProvider == fileProvider);
var fileSystem = new FileProviderRazorProjectFileSystem(accessor, _hostingEnvironment);
var fileSystem = new VirtualRazorProjectFileSystem();
fileSystem.Add(new TestRazorProjectItem(viewPath, "<span name=\"@(User.Id\">"));
var templateEngine = new MvcRazorTemplateEngine(razorEngine, fileSystem);
var codeDocument = templateEngine.CreateCodeDocument(viewPath);
@ -39,7 +32,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
// Assert
var failure = Assert.Single(compilationResult.CompilationFailures);
Assert.Equal(Path.Combine("Views", "Home", "Index.cshtml"), failure.SourceFilePath);
Assert.Equal(viewPath, failure.SourceFilePath);
Assert.Collection(failure.Messages,
message => Assert.StartsWith(
@"Unterminated string literal.",
@ -56,13 +49,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
var viewPath = "/Views/Home/Index.cshtml";
var physicalPath = @"x:\myapp\views\home\index.cshtml";
var fileProvider = new TestFileProvider();
var file = fileProvider.AddFile(viewPath, "<span name=\"@(User.Id\">");
file.PhysicalPath = physicalPath;
var accessor = Mock.Of<IRazorViewEngineFileProviderAccessor>(a => a.FileProvider == fileProvider);
var fileSystem = new VirtualRazorProjectFileSystem();
fileSystem.Add(new TestRazorProjectItem(viewPath, "<span name=\"@(User.Id\">", physicalPath: physicalPath));
var razorEngine = RazorEngine.Create();
var fileSystem = new FileProviderRazorProjectFileSystem(accessor, _hostingEnvironment);
var templateEngine = new MvcRazorTemplateEngine(razorEngine, fileSystem);
var codeDocument = templateEngine.CreateCodeDocument(viewPath);
@ -90,11 +80,8 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
</span>";
var razorEngine = RazorEngine.Create();
var fileProvider = new TestFileProvider();
fileProvider.AddFile(viewPath, fileContent);
var accessor = Mock.Of<IRazorViewEngineFileProviderAccessor>(a => a.FileProvider == fileProvider);
var fileSystem = new FileProviderRazorProjectFileSystem(accessor, _hostingEnvironment);
var fileSystem = new VirtualRazorProjectFileSystem();
fileSystem.Add(new TestRazorProjectItem(viewPath, fileContent));
var templateEngine = new MvcRazorTemplateEngine(razorEngine, fileSystem);
var codeDocument = templateEngine.CreateCodeDocument(viewPath);
@ -113,18 +100,15 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
{
// Arrange
var viewPath = "/Views/Home/Index.cshtml";
var importsFilePath = @"x:\views\_MyImports.cshtml";
var importsPath = "/Views/_MyImports.cshtml";
var fileContent = "@ ";
var importsContent = "@(abc";
var fileProvider = new TestFileProvider();
fileProvider.AddFile(viewPath, fileContent);
var importsFile = fileProvider.AddFile("/Views/_MyImports.cshtml", importsContent);
importsFile.PhysicalPath = importsFilePath;
var accessor = Mock.Of<IRazorViewEngineFileProviderAccessor>(a => a.FileProvider == fileProvider);
var fileSystem = new VirtualRazorProjectFileSystem();
fileSystem.Add(new TestRazorProjectItem(viewPath, fileContent));
fileSystem.Add(new TestRazorProjectItem("/Views/_MyImports.cshtml", importsContent));
var razorEngine = RazorEngine.Create();
var fileSystem = new FileProviderRazorProjectFileSystem(accessor, _hostingEnvironment);
var templateEngine = new MvcRazorTemplateEngine(razorEngine, fileSystem)
{
Options =
@ -143,7 +127,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
compilationResult.CompilationFailures,
failure =>
{
Assert.Equal(Path.Combine("Views", "Home", "Index.cshtml"), failure.SourceFilePath);
Assert.Equal(viewPath, failure.SourceFilePath);
Assert.Collection(failure.Messages,
message =>
{
@ -153,7 +137,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
},
failure =>
{
Assert.Equal(importsFilePath, failure.SourceFilePath);
Assert.Equal(importsPath, failure.SourceFilePath);
Assert.Collection(failure.Messages,
message =>
{
@ -179,7 +163,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
GetRazorDiagnostic("message-3", SourceLocation.Undefined, length: -1),
GetRazorDiagnostic("message-4", new SourceLocation(viewImportsPath, 1, 3, 8), length: 4),
};
var fileProvider = new TestFileProvider();
// Act
var result = CompilationFailedExceptionFactory.Create(codeDocument, diagnostics);

View File

@ -933,9 +933,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
.Setup(p => p.CreateFactory("/Views/_ViewStart.cshtml"))
.Returns(GetPageFactoryResult(() => viewStart));
var fileProvider = new TestFileProvider();
var accessor = Mock.Of<IRazorViewEngineFileProviderAccessor>(a => a.FileProvider == fileProvider);
var fileSystem = new FileProviderRazorProjectFileSystem(accessor, Mock.Of<IHostingEnvironment>());
var fileSystem = new VirtualRazorProjectFileSystem();
var viewEngine = CreateViewEngine(pageFactory.Object, fileSystem: fileSystem);
var context = GetActionContext(_controllerTestContext);
@ -1385,9 +1383,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
Mock.Of<IRazorPageActivator>(),
new HtmlTestEncoder(),
GetOptionsAccessor(expanders: null),
new FileProviderRazorProjectFileSystem(
Mock.Of<IRazorViewEngineFileProviderAccessor>(a => a.FileProvider == new TestFileProvider()),
Mock.Of<IHostingEnvironment>()),
new VirtualRazorProjectFileSystem(),
loggerFactory,
new DiagnosticListener("Microsoft.AspNetCore.Mvc.Razor"));
@ -1972,11 +1968,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
RazorProjectFileSystem fileSystem = null)
{
pageFactory = pageFactory ?? Mock.Of<IRazorPageFactoryProvider>();
if (fileSystem == null)
{
var accessor = Mock.Of<IRazorViewEngineFileProviderAccessor>(a => a.FileProvider == new TestFileProvider());
fileSystem = new FileProviderRazorProjectFileSystem(accessor, Mock.Of<IHostingEnvironment>());
}
fileSystem = fileSystem ?? new VirtualRazorProjectFileSystem();
return new TestableRazorViewEngine(pageFactory, GetOptionsAccessor(expanders), fileSystem);
}

View File

@ -1,10 +1,7 @@
// 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 System.Linq;
using System.Text;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
@ -19,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
public void TryGetPageDirective_FindsTemplate()
{
// Arrange
var projectItem = new TestRazorProjectItem(@"@page ""Some/Path/{value}""
var projectItem = new TestRazorProjectItem("Test.cshtml", @"@page ""Some/Path/{value}""
The rest of the thing");
var sink = new TestSink();
var logger = new TestLogger("logger", sink, enabled: true);
@ -34,7 +31,7 @@ The rest of the thing");
public void TryGetPageDirective_NoNewLine()
{
// Arrange
var projectItem = new TestRazorProjectItem(@"@page ""Some/Path/{value}""");
var projectItem = new TestRazorProjectItem("Test.cshtml", @"@page ""Some/Path/{value}""");
var sink = new TestSink();
var logger = new TestLogger("logger", sink, enabled: true);
@ -48,7 +45,7 @@ The rest of the thing");
public void TryGetPageDirective_JunkBeforeDirective()
{
// Arrange
var projectItem = new TestRazorProjectItem(@"Not a directive @page ""Some/Path/{value}""");
var projectItem = new TestRazorProjectItem("Test.cshtml", @"Not a directive @page ""Some/Path/{value}""");
var sink = new TestSink();
var logger = new TestLogger("logger", sink, enabled: true);
@ -67,7 +64,7 @@ The rest of the thing");
var expected = "The page directive at 'Test.cshtml' is malformed. Please fix the following issues: The 'page' directive expects a string surrounded by double quotes.";
var sink = new TestSink();
var logger = new TestLogger("logger", sink, enabled: true);
var projectItem = new TestRazorProjectItem($@"@page {inTemplate}");
var projectItem = new TestRazorProjectItem("Test.cshtml", $@"@page {inTemplate}");
// Act & Assert
Assert.True(PageDirectiveFeature.TryGetPageDirective(logger, projectItem, out var template));
@ -87,7 +84,7 @@ The rest of the thing");
var expected = "The page directive at 'Test.cshtml' is malformed. Please fix the following issues: The 'page' directive expects a string surrounded by double quotes.";
var sink = new TestSink();
var logger = new TestLogger("logger", sink, enabled: true);
var projectItem = new TestRazorProjectItem(@"@page Some/Path/{value}");
var projectItem = new TestRazorProjectItem("Test.cshtml", @"@page Some/Path/{value}");
// Act & Assert
Assert.True(PageDirectiveFeature.TryGetPageDirective(logger, projectItem, out var template));
@ -105,7 +102,7 @@ The rest of the thing");
public void TryGetPageDirective_NewLineBeforeDirective()
{
// Arrange
var projectItem = new TestRazorProjectItem("\n @page \"Some/Path/{value}\"");
var projectItem = new TestRazorProjectItem("Test.cshtml", "\n @page \"Some/Path/{value}\"");
var sink = new TestSink();
var logger = new TestLogger("logger", sink, enabled: true);
@ -119,7 +116,7 @@ The rest of the thing");
public void TryGetPageDirective_Directive_WithoutPathOrContent()
{
// Arrange
var projectItem = new TestRazorProjectItem(@"@page");
var projectItem = new TestRazorProjectItem("Test.cshtml", @"@page");
// Act & Assert
Assert.True(PageDirectiveFeature.TryGetPageDirective(NullLogger.Instance, projectItem, out var template));
@ -130,7 +127,7 @@ The rest of the thing");
public void TryGetPageDirective_DirectiveWithContent_WithoutPath()
{
// Arrange
var projectItem = new TestRazorProjectItem(@"@page
var projectItem = new TestRazorProjectItem("Test.cshtml", @"@page
Non-path things");
var sink = new TestSink();
var logger = new TestLogger("logger", sink, enabled: true);
@ -145,7 +142,7 @@ Non-path things");
public void TryGetPageDirective_NoDirective()
{
// Arrange
var projectItem = new TestRazorProjectItem(@"This is junk
var projectItem = new TestRazorProjectItem("Test.cshtml", @"This is junk
Nobody will use it");
var sink = new TestSink();
var logger = new TestLogger("logger", sink, enabled: true);
@ -156,34 +153,4 @@ Nobody will use it");
Assert.Empty(sink.Writes);
}
}
public class TestRazorProjectItem : RazorProjectItem
{
private string _content;
public TestRazorProjectItem(string content)
{
_content = content;
}
public override string BasePath => throw new NotImplementedException();
public override bool Exists => throw new NotImplementedException();
public override string FilePath => "Test.cshtml";
public override string PhysicalPath => null;
public override Stream Read()
{
if (_content == null)
{
return null;
}
else
{
return new MemoryStream(Encoding.UTF8.GetBytes(_content));
}
}
}
}

View File

@ -4,19 +4,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
using Microsoft.AspNetCore.Mvc.Razor.Internal;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
using Microsoft.AspNetCore.Razor.Hosting;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;
using static Microsoft.AspNetCore.Razor.Hosting.TestRazorCompiledItem;
@ -141,10 +138,10 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
}),
};
var fileProvider = new TestFileProvider();
fileProvider.AddFile("/Pages/About.cshtml", "some other content");
var fileSystem = new VirtualRazorProjectFileSystem();
fileSystem.Add(new TestRazorProjectItem("/Pages/About.cshtml", "some other content"));
var provider = CreateProvider(descriptors: descriptors, fileProvider: fileProvider);
var provider = CreateProvider(descriptors: descriptors, fileSystem: fileSystem);
var context = new PageRouteModelProviderContext();
// Act
@ -167,11 +164,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
}),
};
var fileProvider = new TestFileProvider();
fileProvider.AddFile("/Pages/About.cshtml", "some content");
fileProvider.AddFile("/Pages/_ViewImports.cshtml", "some import");
var fileSystem = new VirtualRazorProjectFileSystem();
fileSystem.Add(new TestRazorProjectItem("/Pages/About.cshtml", "some content"));
fileSystem.Add(new TestRazorProjectItem("/Pages/_ViewImports.cshtml", "some import"));
var provider = CreateProvider(descriptors: descriptors, fileProvider: fileProvider);
var provider = CreateProvider(descriptors: descriptors, fileSystem: fileSystem);
var context = new PageRouteModelProviderContext();
// Act
@ -196,10 +193,10 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
}),
};
var fileProvider = new TestFileProvider();
fileProvider.AddFile("/Pages/_ViewImports.cshtml", "some other import");
var fileSystem = new VirtualRazorProjectFileSystem();
fileSystem.Add(new TestRazorProjectItem("/Pages/_ViewImports.cshtml", "some other import"));
var provider = CreateProvider(descriptors: descriptors, fileProvider: fileProvider);
var provider = CreateProvider(descriptors: descriptors, fileSystem: fileSystem);
var context = new PageRouteModelProviderContext();
// Act
@ -625,13 +622,10 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
private TestCompiledPageRouteModelProvider CreateProvider(
RazorPagesOptions options = null,
IList<CompiledViewDescriptor> descriptors = null,
TestFileProvider fileProvider = null)
VirtualRazorProjectFileSystem fileSystem = null)
{
options = options ?? new RazorPagesOptions();
fileProvider = fileProvider ?? new TestFileProvider();
var fileSystem = new FileProviderRazorProjectFileSystem(
Mock.Of<IRazorViewEngineFileProviderAccessor>(a => a.FileProvider == fileProvider),
Mock.Of<IHostingEnvironment>(e => e.ContentRootPath == "BasePath"));
fileSystem = fileSystem ?? new VirtualRazorProjectFileSystem();
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, fileSystem);
var provider = new TestCompiledPageRouteModelProvider(

View File

@ -192,18 +192,15 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
.Setup(f => f.CreateFactory("/_ViewStart.cshtml"))
.Returns(new RazorPageFactoryResult(new CompiledViewDescriptor(), factory2));
var fileProvider = new TestFileProvider();
fileProvider.AddFile("/Home/Path1/_ViewStart.cshtml", "content1");
fileProvider.AddFile("/_ViewStart.cshtml", "content2");
var accessor = Mock.Of<IRazorViewEngineFileProviderAccessor>(a => a.FileProvider == fileProvider);
var defaultFileSystem = new FileProviderRazorProjectFileSystem(accessor, _hostingEnvironment);
var fileSystem = new VirtualRazorProjectFileSystem();
fileSystem.Add(new TestRazorProjectItem("/Home/Path1/_ViewStart.cshtml", "content1"));
fileSystem.Add(new TestRazorProjectItem("/_ViewStart.cshtml", "content2"));
var invokerProvider = CreateInvokerProvider(
loader.Object,
CreateActionDescriptorCollection(descriptor),
razorPageFactoryProvider: razorPageFactoryProvider.Object,
fileSystem: defaultFileSystem);
fileSystem: fileSystem);
var context = new ActionInvokerProviderContext(new ActionContext()
{

View File

@ -1,35 +1,24 @@
// 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.Linq;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.Extensions.FileProviders;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
{
public class RazorProjectPageRouteModelProviderTest
{
private readonly IHostingEnvironment _hostingEnvironment = Mock.Of<IHostingEnvironment>(e => e.ContentRootPath == "BasePath");
[Fact]
public void OnProvidersExecuting_ReturnsPagesWithPageDirective()
{
// Arrange
var fileProvider = new TestFileProvider();
var file1 = fileProvider.AddFile("/Pages/Home.cshtml", "@page");
var file2 = fileProvider.AddFile("/Pages/Test.cshtml", "Hello world");
var dir1 = fileProvider.AddDirectoryContent("/Pages", new IFileInfo[] { file1, file2 });
fileProvider.AddDirectoryContent("/", new[] { dir1 });
var fileSystem = new TestRazorProjectFileSystem(fileProvider, _hostingEnvironment);
var fileSystem = new VirtualRazorProjectFileSystem();
fileSystem.Add(new TestRazorProjectItem("/Pages/Home.cshtml", "@page"));
fileSystem.Add(new TestRazorProjectItem("/Pages/Test.cshtml", "Hello world"));
var optionsManager = Options.Create(new RazorPagesOptions());
optionsManager.Value.RootDirectory = "/";
@ -60,18 +49,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_AddsPagesUnderAreas()
{
// Arrange
var fileProvider = new TestFileProvider();
var file1 = fileProvider.AddFile("Categories.cshtml", "@page");
var file2 = fileProvider.AddFile("Index.cshtml", "@page");
var file3 = fileProvider.AddFile("List.cshtml", "@page \"{sortOrder?}\"");
var file4 = fileProvider.AddFile("_ViewStart.cshtml", "@page");
var manageDir = fileProvider.AddDirectoryContent("/Areas/Products/Pages/Manage", new[] { file1 });
var pagesDir = fileProvider.AddDirectoryContent("/Areas/Products/Pages", new IFileInfo[] { manageDir, file2, file3, file4 });
var productsDir = fileProvider.AddDirectoryContent("/Areas/Products", new[] { pagesDir });
var areasDir = fileProvider.AddDirectoryContent("/Areas", new[] { productsDir });
var rootDir = fileProvider.AddDirectoryContent("/", new[] { areasDir });
var fileSystem = new TestRazorProjectFileSystem(fileProvider, _hostingEnvironment);
var fileSystem = new VirtualRazorProjectFileSystem();
fileSystem.Add(new TestRazorProjectItem("/Areas/Products/Pages/Manage/Categories.cshtml", "@page"));
fileSystem.Add(new TestRazorProjectItem("/Areas/Products/Pages/Index.cshtml", "@page"));
fileSystem.Add(new TestRazorProjectItem("/Areas/Products/Pages/List.cshtml", "@page \"{sortOrder?}\""));
fileSystem.Add(new TestRazorProjectItem("/Areas/Products/Pages/_ViewStart.cshtml", "@page"));
var optionsManager = Options.Create(new RazorPagesOptions { AllowAreas = true });
var provider = new RazorProjectPageRouteModelProvider(fileSystem, optionsManager, NullLoggerFactory.Instance);
@ -82,24 +64,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
// Assert
Assert.Collection(context.RouteModels,
model =>
{
Assert.Equal("/Areas/Products/Pages/Manage/Categories.cshtml", model.RelativePath);
Assert.Equal("/Manage/Categories", model.ViewEnginePath);
Assert.Collection(model.Selectors,
selector => Assert.Equal("Products/Manage/Categories", selector.AttributeRouteModel.Template));
Assert.Collection(model.RouteValues.OrderBy(k => k.Key),
kvp =>
{
Assert.Equal("area", kvp.Key);
Assert.Equal("Products", kvp.Value);
},
kvp =>
{
Assert.Equal("page", kvp.Key);
Assert.Equal("/Manage/Categories", kvp.Value);
});
},
model =>
{
Assert.Equal("/Areas/Products/Pages/Index.cshtml", model.RelativePath);
@ -136,6 +100,24 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
Assert.Equal("page", kvp.Key);
Assert.Equal("/List", kvp.Value);
});
},
model =>
{
Assert.Equal("/Areas/Products/Pages/Manage/Categories.cshtml", model.RelativePath);
Assert.Equal("/Manage/Categories", model.ViewEnginePath);
Assert.Collection(model.Selectors,
selector => Assert.Equal("Products/Manage/Categories", selector.AttributeRouteModel.Template));
Assert.Collection(model.RouteValues.OrderBy(k => k.Key),
kvp =>
{
Assert.Equal("area", kvp.Key);
Assert.Equal("Products", kvp.Value);
},
kvp =>
{
Assert.Equal("page", kvp.Key);
Assert.Equal("/Manage/Categories", kvp.Value);
});
});
}
@ -143,19 +125,12 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_DoesNotAddPagesUnderAreas_WhenFeatureIsDisabled()
{
// Arrange
var fileProvider = new TestFileProvider();
var file1 = fileProvider.AddFile("Categories.cshtml", "@page");
var file2 = fileProvider.AddFile("Index.cshtml", "@page");
var file3 = fileProvider.AddFile("List.cshtml", "@page \"{sortOrder?}\"");
var file4 = fileProvider.AddFile("About.cshtml", "@page");
var manageDir = fileProvider.AddDirectoryContent("/Areas/Products/Pages/Manage", new[] { file1 });
var areaPagesDir = fileProvider.AddDirectoryContent("/Areas/Products/Pages", new IFileInfo[] { manageDir, file2, file3, });
var productsDir = fileProvider.AddDirectoryContent("/Areas/Products", new[] { areaPagesDir });
var areasDir = fileProvider.AddDirectoryContent("/Areas", new[] { productsDir });
var pagesDir = fileProvider.AddDirectoryContent("/Pages", new[] { file4 });
var rootDir = fileProvider.AddDirectoryContent("/", new[] { areasDir, pagesDir });
var fileSystem = new TestRazorProjectFileSystem(fileProvider, _hostingEnvironment);
var fileSystem = new VirtualRazorProjectFileSystem();
fileSystem.Add(new TestRazorProjectItem("/Areas/Products/Pages/Manage/Categories.cshtml", "@page"));
fileSystem.Add(new TestRazorProjectItem("/Areas/Products/Pages/Index.cshtml", "@page"));
fileSystem.Add(new TestRazorProjectItem("/Areas/Products/Pages/List.cshtml", "@page \"{sortOrder?}\""));
fileSystem.Add(new TestRazorProjectItem("/Areas/Products/Pages/_ViewStart.cshtml", "@page"));
fileSystem.Add(new TestRazorProjectItem("/Pages/About.cshtml", "@page"));
var optionsManager = Options.Create(new RazorPagesOptions { AllowAreas = false });
var provider = new RazorProjectPageRouteModelProvider(fileSystem, optionsManager, NullLoggerFactory.Instance);
@ -179,17 +154,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_DoesNotAddAreaAndNonAreaRoutesForAPage()
{
// Arrange
var fileProvider = new TestFileProvider();
var conformingFileUnderAreasDirectory = fileProvider.AddFile("Categories.cshtml", "@page");
var fileSystem = new VirtualRazorProjectFileSystem();
fileSystem.Add(new TestRazorProjectItem("/Areas/Products/Categories.cshtml", "@page"));
// We shouldn't add a route for this.
var nonConformingFileUnderAreasDirectory = fileProvider.AddFile("Home.cshtml", "@page");
var rootFile = fileProvider.AddFile("About.cshtml", "@page");
var productsDir = fileProvider.AddDirectoryContent("/Areas/Products", new[] { conformingFileUnderAreasDirectory });
var areasDir = fileProvider.AddDirectoryContent("/Areas", new IFileInfo[] { productsDir, nonConformingFileUnderAreasDirectory });
var rootDir = fileProvider.AddDirectoryContent("/", new IFileInfo[] { areasDir, rootFile });
var fileSystem = new TestRazorProjectFileSystem(fileProvider, _hostingEnvironment);
fileSystem.Add(new TestRazorProjectItem("/Areas/Home.cshtml", "@page"));
fileSystem.Add(new TestRazorProjectItem("/About.cshtml", "@page"));
var optionsManager = Options.Create(new RazorPagesOptions
{
@ -242,16 +211,10 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_AddsMultipleSelectorsForIndexPages()
{
// Arrange
var fileProvider = new TestFileProvider();
var file1 = fileProvider.AddFile("/Pages/Index.cshtml", "@page");
var file2 = fileProvider.AddFile("/Pages/Test.cshtml", "Hello world");
var file3 = fileProvider.AddFile("/Pages/Admin/Index.cshtml", "@page \"test\"");
var dir2 = fileProvider.AddDirectoryContent("/Pages/Admin", new[] { file3 });
var dir1 = fileProvider.AddDirectoryContent("/Pages", new IFileInfo[] { dir2, file1, file2 });
fileProvider.AddDirectoryContent("/", new[] { dir1 });
var fileSystem = new TestRazorProjectFileSystem(fileProvider, _hostingEnvironment);
var fileSystem = new VirtualRazorProjectFileSystem();
fileSystem.Add(new TestRazorProjectItem("/Pages/Index.cshtml", "@page"));
fileSystem.Add(new TestRazorProjectItem("/Pages/Test.cshtml", "Hello world"));
fileSystem.Add(new TestRazorProjectItem("/Pages/Admin/Index.cshtml", "@page \"test\""));
var optionsManager = Options.Create(new RazorPagesOptions());
optionsManager.Value.RootDirectory = "/";
@ -263,14 +226,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
// Assert
Assert.Collection(context.RouteModels,
model =>
{
Assert.Equal("/Pages/Admin/Index.cshtml", model.RelativePath);
Assert.Equal("/Pages/Admin/Index", model.ViewEnginePath);
Assert.Collection(model.Selectors,
selector => Assert.Equal("Pages/Admin/Index/test", selector.AttributeRouteModel.Template),
selector => Assert.Equal("Pages/Admin/test", selector.AttributeRouteModel.Template));
},
model =>
{
Assert.Equal("/Pages/Index.cshtml", model.RelativePath);
@ -278,6 +233,14 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
Assert.Collection(model.Selectors,
selector => Assert.Equal("Pages/Index", selector.AttributeRouteModel.Template),
selector => Assert.Equal("Pages", selector.AttributeRouteModel.Template));
},
model =>
{
Assert.Equal("/Pages/Admin/Index.cshtml", model.RelativePath);
Assert.Equal("/Pages/Admin/Index", model.ViewEnginePath);
Assert.Collection(model.Selectors,
selector => Assert.Equal("Pages/Admin/Index/test", selector.AttributeRouteModel.Template),
selector => Assert.Equal("Pages/Admin/test", selector.AttributeRouteModel.Template));
});
}
@ -285,11 +248,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_AllowsRouteTemplateWithOverridePattern()
{
// Arrange
var fileProvider = new TestFileProvider();
var file = fileProvider.AddFile("/Index.cshtml", "@page \"/custom-route\"");
fileProvider.AddDirectoryContent("/", new[] { file });
var fileSystem = new TestRazorProjectFileSystem(fileProvider, _hostingEnvironment);
var fileSystem = new VirtualRazorProjectFileSystem();
fileSystem.Add(new TestRazorProjectItem("/Index.cshtml", "@page \"/custom-route\""));
var optionsManager = Options.Create(new RazorPagesOptions());
optionsManager.Value.RootDirectory = "/";
@ -316,16 +276,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_SkipsPagesStartingWithUnderscore()
{
// Arrange
var fileProvider = new TestFileProvider();
var dir1 = fileProvider.AddDirectoryContent("/Pages",
new[]
{
fileProvider.AddFile("/Pages/Home.cshtml", "@page"),
fileProvider.AddFile("/Pages/_Layout.cshtml", "@page")
});
fileProvider.AddDirectoryContent("/", new[] { dir1 });
var fileSystem = new TestRazorProjectFileSystem(fileProvider, _hostingEnvironment);
var fileSystem = new VirtualRazorProjectFileSystem();
fileSystem.Add(new TestRazorProjectItem("/Pages/Home.cshtml", "@page"));
fileSystem.Add(new TestRazorProjectItem("/Pages/_Layout.cshtml", "@page"));
var optionsManager = Options.Create(new RazorPagesOptions());
optionsManager.Value.RootDirectory = "/";
@ -347,23 +300,12 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_DiscoversFilesUnderBasePath()
{
// Arrange
var fileProvider = new TestFileProvider();
var dir1 = fileProvider.AddDirectoryContent("/Pages",
new[]
{
fileProvider.AddFile("/Pages/Index.cshtml", "@page"),
fileProvider.AddFile("/Pages/_Layout.cshtml", "@page")
});
var dir2 = fileProvider.AddDirectoryContent("/NotPages",
new[]
{
fileProvider.AddFile("/NotPages/Index.cshtml", "@page"),
fileProvider.AddFile("/NotPages/_Layout.cshtml", "@page")
});
var rootFile = fileProvider.AddFile("/Index.cshtml", "@page");
fileProvider.AddDirectoryContent("/", new IFileInfo[] { rootFile, dir1, dir2 });
var fileSystem = new TestRazorProjectFileSystem(fileProvider, _hostingEnvironment);
var fileSystem = new VirtualRazorProjectFileSystem();
fileSystem.Add(new TestRazorProjectItem("/Pages/Index.cshtml", "@page"));
fileSystem.Add(new TestRazorProjectItem("/Pages/_Layout.cshtml", "@page"));
fileSystem.Add(new TestRazorProjectItem("/NotPages/Index.cshtml", "@page"));
fileSystem.Add(new TestRazorProjectItem("/NotPages/_Layout.cshtml", "@page"));
fileSystem.Add(new TestRazorProjectItem("/Index.cshtml", "@page"));
var optionsManager = Options.Create(new RazorPagesOptions());
optionsManager.Value.RootDirectory = "/Pages";
@ -385,14 +327,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_DoesNotAddPageDirectivesIfItAlreadyExists()
{
// Arrange
var fileProvider = new TestFileProvider();
var file1 = fileProvider.AddFile("/Pages/Home.cshtml", "@page");
var file2 = fileProvider.AddFile("/Pages/Test.cshtml", "@page");
var dir1 = fileProvider.AddDirectoryContent("/Pages", new IFileInfo[] { file1, file2 });
fileProvider.AddDirectoryContent("/", new[] { dir1 });
var fileSystem = new TestRazorProjectFileSystem(fileProvider, _hostingEnvironment);
var fileSystem = new VirtualRazorProjectFileSystem();
fileSystem.Add(new TestRazorProjectItem("/Pages/Home.cshtml", "@page"));
fileSystem.Add(new TestRazorProjectItem("/Pages/Test.cshtml", "@page"));
var optionsManager = Options.Create(new RazorPagesOptions());
optionsManager.Value.RootDirectory = "/";

View File

@ -12,6 +12,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Html.Abstractions" Version="$(MicrosoftAspNetCoreHtmlAbstractionsPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Runtime" Version="$(MicrosoftAspNetCoreRazorRuntimePackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="$(MicrosoftAspNetCoreRazorLanguagePackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Abstractions" Version="$(MicrosoftExtensionsFileProvidersAbstractionsPackageVersion)" />

View File

@ -0,0 +1,44 @@
// 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 System.Text;
namespace Microsoft.AspNetCore.Razor.Language
{
public class TestRazorProjectItem : RazorProjectItem
{
public TestRazorProjectItem(
string filePath,
string content = "Default content",
string physicalPath = null,
string relativePhysicalPath = null,
string basePath = "/")
{
FilePath = filePath;
PhysicalPath = physicalPath;
RelativePhysicalPath = relativePhysicalPath;
BasePath = basePath;
Content = content;
}
public override string BasePath { get; }
public override string FilePath { get; }
public override string PhysicalPath { get; }
public override string RelativePhysicalPath { get; }
public override bool Exists { get; } = true;
public string Content { get; set; }
public override Stream Read()
{
var stream = new MemoryStream(Encoding.UTF8.GetBytes(Content));
return stream;
}
}
}

View File

@ -0,0 +1,233 @@
// 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.Diagnostics;
using System.IO;
using System.Linq;
namespace Microsoft.AspNetCore.Razor.Language
{
public class VirtualRazorProjectFileSystem : RazorProjectFileSystem
{
private readonly DirectoryNode _root = new DirectoryNode("/");
public override IEnumerable<RazorProjectItem> EnumerateItems(string basePath)
{
basePath = NormalizeAndEnsureValidPath(basePath);
var directory = _root.GetDirectory(basePath);
return directory?.EnumerateItems() ?? Enumerable.Empty<RazorProjectItem>();
}
public override RazorProjectItem GetItem(string path)
{
path = NormalizeAndEnsureValidPath(path);
return _root.GetItem(path) ?? new NotFoundProjectItem(string.Empty, path);
}
public void Add(RazorProjectItem projectItem)
{
if (projectItem == null)
{
throw new ArgumentNullException(nameof(projectItem));
}
var filePath = NormalizeAndEnsureValidPath(projectItem.FilePath);
_root.AddFile(new FileNode(filePath, projectItem));
}
// Internal for testing
[DebuggerDisplay("{Path}")]
internal class DirectoryNode
{
public DirectoryNode(string path)
{
Path = path;
}
public string Path { get; }
public List<DirectoryNode> Directories { get; } = new List<DirectoryNode>();
public List<FileNode> Files { get; } = new List<FileNode>();
public void AddFile(FileNode fileNode)
{
var filePath = fileNode.Path;
if (!filePath.StartsWith(Path, StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException($"File {fileNode.Path} does not belong to {Path}.");
}
// Look for the first / that appears in the path after the current directory path.
var directoryPath = GetDirectoryPath(filePath);
var directory = GetOrAddDirectory(this, directoryPath, createIfNotExists: true);
Debug.Assert(directory != null);
directory.Files.Add(fileNode);
}
public DirectoryNode GetDirectory(string path)
{
if (!path.StartsWith(Path, StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException($"File {path} does not belong to {Path}.");
}
return GetOrAddDirectory(this, path);
}
public IEnumerable<RazorProjectItem> EnumerateItems()
{
foreach (var file in Files)
{
yield return file.ProjectItem;
}
foreach (var directory in Directories)
{
foreach (var file in directory.EnumerateItems())
{
yield return file;
}
}
}
public RazorProjectItem GetItem(string path)
{
if (!path.StartsWith(Path, StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException($"File {path} does not belong to {Path}.");
}
var directoryPath = GetDirectoryPath(path);
var directory = GetOrAddDirectory(this, directoryPath);
if (directory == null)
{
return null;
}
foreach (var file in directory.Files)
{
var filePath = file.Path;
var directoryLength = directory.Path.Length;
// path, filePath -> /Views/Home/Index.cshtml
// directory.Path -> /Views/Home/
// We only need to match the file name portion since we've already matched the directory segment.
if (string.Compare(path, directoryLength, filePath, directoryLength, path.Length - directoryLength, StringComparison.OrdinalIgnoreCase) == 0)
{
return file.ProjectItem;
}
}
return null;
}
private static string GetDirectoryPath(string path)
{
// /dir1/dir2/file.cshtml -> /dir1/dir2/
var fileNameIndex = path.LastIndexOf('/');
if (fileNameIndex == -1)
{
return path;
}
return path.Substring(0, fileNameIndex + 1);
}
private static DirectoryNode GetOrAddDirectory(
DirectoryNode directory,
string path,
bool createIfNotExists = false)
{
Debug.Assert(!string.IsNullOrEmpty(path));
if (path[path.Length - 1] != '/')
{
path += '/';
}
int index;
while ((index = path.IndexOf('/', directory.Path.Length)) != -1 && index != path.Length)
{
var subDirectory = FindSubDirectory(directory, path);
if (subDirectory == null)
{
if (createIfNotExists)
{
var directoryPath = path.Substring(0, index + 1); // + 1 to include trailing slash
subDirectory = new DirectoryNode(directoryPath);
directory.Directories.Add(subDirectory);
}
else
{
return null;
}
}
directory = subDirectory;
}
return directory;
}
private static DirectoryNode FindSubDirectory(DirectoryNode parentDirectory, string path)
{
for (var i = 0; i < parentDirectory.Directories.Count; i++)
{
// ParentDirectory.Path -> /Views/Home/
// CurrentDirectory.Path -> /Views/Home/SubDir/
// Path -> /Views/Home/SubDir/MorePath/File.cshtml
// Each invocation of FindSubDirectory returns the immediate subdirectory along the path to the file.
var currentDirectory = parentDirectory.Directories[i];
var directoryPath = currentDirectory.Path;
var startIndex = parentDirectory.Path.Length;
var directoryNameLength = directoryPath.Length - startIndex;
if (string.Compare(path, startIndex, directoryPath, startIndex, directoryPath.Length - startIndex, StringComparison.OrdinalIgnoreCase) == 0)
{
return currentDirectory;
}
}
return null;
}
}
// Internal for testing
[DebuggerDisplay("{Path}")]
internal struct FileNode
{
public FileNode(string path, RazorProjectItem projectItem)
{
Path = path;
ProjectItem = projectItem;
}
public string Path { get; }
public RazorProjectItem ProjectItem { get; }
}
private class NotFoundProjectItem : RazorProjectItem
{
public NotFoundProjectItem(string basePath, string path)
{
BasePath = basePath;
FilePath = path;
}
public override string BasePath { get; }
public override string FilePath { get; }
public override bool Exists => false;
public override string PhysicalPath => throw new NotSupportedException();
public override Stream Read() => throw new NotSupportedException();
}
}
}