Add MVC support for RazorProjectEngine.

- Make `RazorProjectEngine` call paths for all feature registrations.
- Add `DefaultMvcImportFeature` for latest and 1.X MVC.
- Ported `AddTargetExtension` and `AddDirective` to `RazorProjectEngineBuilderExtensions`.
- Added tests and a test file system project type.
- Moved obsolete `IRazorEngineBuilder` methods to the bottom of each file. Will actually obsolete the methods once `RazorProjectEngine` is working end-to-end.

#1828
This commit is contained in:
N. Taylor Mullen 2018-01-22 18:10:21 -08:00
parent bc6d726c36
commit 771a7e35a4
21 changed files with 823 additions and 12 deletions

View File

@ -0,0 +1,84 @@
// 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 System.Linq;
using System.Text;
using Microsoft.AspNetCore.Razor.Language;
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
{
internal class DefaultMvcImportFeature : RazorProjectEngineFeatureBase, IRazorImportFeature
{
private const string ImportsFileName = "_ViewImports.cshtml";
public IReadOnlyList<RazorSourceDocument> GetImports(string sourceFilePath)
{
if (string.IsNullOrEmpty(sourceFilePath))
{
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpy, nameof(sourceFilePath));
}
var imports = new List<RazorSourceDocument>();
AddDefaultDirectivesImport(imports);
// We add hierarchical imports second so any default directive imports can be overridden.
AddHierarchicalImports(sourceFilePath, imports);
return imports;
}
// Internal for testing
internal static void AddDefaultDirectivesImport(List<RazorSourceDocument> imports)
{
using (var stream = new MemoryStream())
using (var writer = new StreamWriter(stream, Encoding.UTF8))
{
writer.WriteLine("@using System");
writer.WriteLine("@using System.Collections.Generic");
writer.WriteLine("@using System.Linq");
writer.WriteLine("@using System.Threading.Tasks");
writer.WriteLine("@using Microsoft.AspNetCore.Mvc");
writer.WriteLine("@using Microsoft.AspNetCore.Mvc.Rendering");
writer.WriteLine("@using Microsoft.AspNetCore.Mvc.ViewFeatures");
writer.WriteLine("@inject global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper<TModel> Html");
writer.WriteLine("@inject global::Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper Json");
writer.WriteLine("@inject global::Microsoft.AspNetCore.Mvc.IViewComponentHelper Component");
writer.WriteLine("@inject global::Microsoft.AspNetCore.Mvc.IUrlHelper Url");
writer.WriteLine("@inject global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider ModelExpressionProvider");
writer.WriteLine("@addTagHelper Microsoft.AspNetCore.Mvc.Razor.TagHelpers.UrlResolutionTagHelper, Microsoft.AspNetCore.Mvc.Razor");
writer.Flush();
stream.Position = 0;
var defaultMvcImports = RazorSourceDocument.ReadFrom(stream, fileName: null, encoding: Encoding.UTF8);
imports.Add(defaultMvcImports);
}
}
// Internal for testing
internal void AddHierarchicalImports(string sourceFilePath, List<RazorSourceDocument> imports)
{
// We want items in descending order. FindHierarchicalItems returns items in ascending order.
var importProjectItems = ProjectEngine.FileSystem.FindHierarchicalItems(sourceFilePath, ImportsFileName).Reverse();
foreach (var importProjectItem in importProjectItems)
{
RazorSourceDocument importSourceDocument;
if (importProjectItem.Exists)
{
importSourceDocument = RazorSourceDocument.ReadFrom(importProjectItem);
}
else
{
// File doesn't exist on disk so just add a marker source document as an identifier for "there could be something here".
var sourceDocumentProperties = new RazorSourceDocumentProperties(importProjectItem.FilePath, importProjectItem.RelativePhysicalPath);
importSourceDocument = RazorSourceDocument.Create(string.Empty, sourceDocumentProperties);
}
imports.Add(importSourceDocument);
}
}
}
}

View File

@ -24,8 +24,13 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
builder.Description = Resources.InjectDirective_Description;
});
public static IRazorEngineBuilder Register(IRazorEngineBuilder builder)
public static RazorProjectEngineBuilder Register(RazorProjectEngineBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder.AddDirective(Directive);
builder.Features.Add(new Pass());
builder.AddTargetExtension(new InjectTargetExtension());
@ -99,5 +104,20 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
}
}
}
#region Obsolete
public static IRazorEngineBuilder Register(IRazorEngineBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder.AddDirective(Directive);
builder.Features.Add(new Pass());
builder.AddTargetExtension(new InjectTargetExtension());
return builder;
}
#endregion
}
}

View File

@ -21,8 +21,13 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
builder.Description = Resources.ModelDirective_Description;
});
public static IRazorEngineBuilder Register(IRazorEngineBuilder builder)
public static RazorProjectEngineBuilder Register(RazorProjectEngineBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder.AddDirective(Directive);
builder.Features.Add(new Pass(builder.DesignTime));
return builder;
@ -128,5 +133,19 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
}
}
}
#region Obsolete
public static IRazorEngineBuilder Register(IRazorEngineBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder.AddDirective(Directive);
builder.Features.Add(new Pass(builder.DesignTime));
return builder;
}
#endregion
}
}

View File

@ -9,8 +9,68 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
{
public static class RazorExtensions
{
public static void Register(RazorProjectEngineBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
EnsureDesignTime(builder);
InjectDirective.Register(builder);
ModelDirective.Register(builder);
FunctionsDirective.Register(builder);
InheritsDirective.Register(builder);
// Register section directive with the 1.x compatible target extension.
builder.AddDirective(SectionDirective.Directive);
builder.Features.Add(new SectionDirectivePass());
builder.AddTargetExtension(new LegacySectionTargetExtension());
builder.AddTargetExtension(new TemplateTargetExtension()
{
TemplateTypeName = "global::Microsoft.AspNetCore.Mvc.Razor.HelperResult",
});
builder.Features.Add(new ModelExpressionPass());
builder.Features.Add(new MvcViewDocumentClassifierPass());
builder.SetImportFeature(new DefaultMvcImportFeature());
}
public static void RegisterViewComponentTagHelpers(RazorProjectEngineBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
EnsureDesignTime(builder);
builder.Features.Add(new ViewComponentTagHelperPass());
builder.AddTargetExtension(new ViewComponentTagHelperTargetExtension());
}
private static void EnsureDesignTime(RazorProjectEngineBuilder builder)
{
if (builder.DesignTime)
{
return;
}
throw new NotSupportedException(Resources.RuntimeCodeGenerationNotSupported);
}
#region Obsolete
public static void Register(IRazorEngineBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
EnsureDesignTime(builder);
InjectDirective.Register(builder);
@ -35,6 +95,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
public static void RegisterViewComponentTagHelpers(IRazorEngineBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
EnsureDesignTime(builder);
builder.Features.Add(new ViewComponentTagHelperPass());
@ -50,5 +115,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
throw new NotSupportedException(Resources.RuntimeCodeGenerationNotSupported);
}
#endregion
}
}

View File

@ -0,0 +1,86 @@
// 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 System.Linq;
using System.Text;
using Microsoft.AspNetCore.Razor.Language;
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
{
internal class DefaultMvcImportFeature : RazorProjectEngineFeatureBase, IRazorImportFeature
{
private const string ImportsFileName = "_ViewImports.cshtml";
public IReadOnlyList<RazorSourceDocument> GetImports(string sourceFilePath)
{
if (string.IsNullOrEmpty(sourceFilePath))
{
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpy, nameof(sourceFilePath));
}
var imports = new List<RazorSourceDocument>();
AddDefaultDirectivesImport(imports);
// We add hierarchical imports second so any default directive imports can be overridden.
AddHierarchicalImports(sourceFilePath, imports);
return imports;
}
// Internal for testing
internal static void AddDefaultDirectivesImport(List<RazorSourceDocument> imports)
{
using (var stream = new MemoryStream())
using (var writer = new StreamWriter(stream, Encoding.UTF8))
{
writer.WriteLine("@using System");
writer.WriteLine("@using System.Collections.Generic");
writer.WriteLine("@using System.Linq");
writer.WriteLine("@using System.Threading.Tasks");
writer.WriteLine("@using Microsoft.AspNetCore.Mvc");
writer.WriteLine("@using Microsoft.AspNetCore.Mvc.Rendering");
writer.WriteLine("@using Microsoft.AspNetCore.Mvc.ViewFeatures");
writer.WriteLine("@inject global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper<TModel> Html");
writer.WriteLine("@inject global::Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper Json");
writer.WriteLine("@inject global::Microsoft.AspNetCore.Mvc.IViewComponentHelper Component");
writer.WriteLine("@inject global::Microsoft.AspNetCore.Mvc.IUrlHelper Url");
writer.WriteLine("@inject global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider ModelExpressionProvider");
writer.WriteLine("@addTagHelper Microsoft.AspNetCore.Mvc.Razor.TagHelpers.UrlResolutionTagHelper, Microsoft.AspNetCore.Mvc.Razor");
writer.WriteLine("@addTagHelper Microsoft.AspNetCore.Mvc.Razor.TagHelpers.HeadTagHelper, Microsoft.AspNetCore.Mvc.Razor");
writer.WriteLine("@addTagHelper Microsoft.AspNetCore.Mvc.Razor.TagHelpers.BodyTagHelper, Microsoft.AspNetCore.Mvc.Razor");
writer.Flush();
stream.Position = 0;
var defaultMvcImports = RazorSourceDocument.ReadFrom(stream, fileName: null, encoding: Encoding.UTF8);
imports.Add(defaultMvcImports);
}
}
// Internal for testing
internal void AddHierarchicalImports(string sourceFilePath, List<RazorSourceDocument> imports)
{
// We want items in descending order. FindHierarchicalItems returns items in ascending order.
var importProjectItems = ProjectEngine.FileSystem.FindHierarchicalItems(sourceFilePath, ImportsFileName).Reverse();
foreach (var importProjectItem in importProjectItems)
{
RazorSourceDocument importSourceDocument;
if (importProjectItem.Exists)
{
importSourceDocument = RazorSourceDocument.ReadFrom(importProjectItem);
}
else
{
// File doesn't exist on disk so just add a marker source document as an identifier for "there could be something here".
var sourceDocumentProperties = new RazorSourceDocumentProperties(importProjectItem.FilePath, importProjectItem.RelativePhysicalPath);
importSourceDocument = RazorSourceDocument.Create(string.Empty, sourceDocumentProperties);
}
imports.Add(importSourceDocument);
}
}
}
}

View File

@ -24,8 +24,13 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
builder.Description = Resources.InjectDirective_Description;
});
public static IRazorEngineBuilder Register(IRazorEngineBuilder builder)
public static RazorProjectEngineBuilder Register(RazorProjectEngineBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder.AddDirective(Directive);
builder.Features.Add(new Pass());
builder.AddTargetExtension(new InjectTargetExtension());
@ -99,5 +104,20 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
}
}
}
#region Obsolete
public static IRazorEngineBuilder Register(IRazorEngineBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder.AddDirective(Directive);
builder.Features.Add(new Pass());
builder.AddTargetExtension(new InjectTargetExtension());
return builder;
}
#endregion
}
}

View File

@ -21,8 +21,13 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
builder.Description = Resources.ModelDirective_Description;
});
public static IRazorEngineBuilder Register(IRazorEngineBuilder builder)
public static RazorProjectEngineBuilder Register(RazorProjectEngineBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder.AddDirective(Directive);
builder.Features.Add(new Pass(builder.DesignTime));
return builder;
@ -135,5 +140,19 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
}
}
}
#region Obsolete
public static IRazorEngineBuilder Register(IRazorEngineBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder.AddDirective(Directive);
builder.Features.Add(new Pass(builder.DesignTime));
return builder;
}
#endregion
}
}

View File

@ -26,7 +26,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
builder.Description = Resources.NamespaceDirective_Description;
});
public static void Register(IRazorEngineBuilder builder)
public static void Register(RazorProjectEngineBuilder builder)
{
if (builder == null)
{
@ -186,5 +186,18 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
base.VisitDirective(node);
}
}
#region Obsolete
public static void Register(IRazorEngineBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException();
}
builder.AddDirective(Directive);
builder.Features.Add(new Pass());
}
#endregion
}
}

View File

@ -32,8 +32,13 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
public IntermediateNode DirectiveNode { get; }
public static IRazorEngineBuilder Register(IRazorEngineBuilder builder)
public static RazorProjectEngineBuilder Register(RazorProjectEngineBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder.AddDirective(Directive);
return builder;
}
@ -98,5 +103,18 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
}
}
}
#region Obsolete
public static IRazorEngineBuilder Register(IRazorEngineBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder.AddDirective(Directive);
return builder;
}
#endregion
}
}

View File

@ -1,6 +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 Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Extensions;
@ -8,8 +9,51 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
{
public static class RazorExtensions
{
public static void Register(RazorProjectEngineBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
InjectDirective.Register(builder);
ModelDirective.Register(builder);
NamespaceDirective.Register(builder);
PageDirective.Register(builder);
FunctionsDirective.Register(builder);
InheritsDirective.Register(builder);
SectionDirective.Register(builder);
builder.AddTargetExtension(new ViewComponentTagHelperTargetExtension());
builder.AddTargetExtension(new TemplateTargetExtension()
{
TemplateTypeName = "global::Microsoft.AspNetCore.Mvc.Razor.HelperResult",
});
builder.Features.Add(new ModelExpressionPass());
builder.Features.Add(new PagesPropertyInjectionPass());
builder.Features.Add(new ViewComponentTagHelperPass());
builder.Features.Add(new RazorPageDocumentClassifierPass());
builder.Features.Add(new MvcViewDocumentClassifierPass());
if (!builder.DesignTime)
{
builder.Features.Add(new AssemblyAttributeInjectionPass());
builder.Features.Add(new InstrumentationPass());
}
builder.SetImportFeature(new DefaultMvcImportFeature());
}
#region Obsolete
public static void Register(IRazorEngineBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
InjectDirective.Register(builder);
ModelDirective.Register(builder);
NamespaceDirective.Register(builder);
@ -37,5 +81,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
builder.Features.Add(new InstrumentationPass());
}
}
#endregion
}
}

View File

@ -6,10 +6,8 @@ using System.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Language
{
internal class DefaultRazorImportFeature : IRazorImportFeature
internal class DefaultRazorImportFeature : RazorProjectEngineFeatureBase, IRazorImportFeature
{
public RazorProjectEngine ProjectEngine { get; set; }
public IReadOnlyList<RazorSourceDocument> GetImports(string sourceFilePath) => Array.Empty<RazorSourceDocument>();
}
}

View File

@ -1,6 +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 Microsoft.AspNetCore.Razor.Language.Legacy;
namespace Microsoft.AspNetCore.Razor.Language.Extensions
@ -15,10 +16,28 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
builder.Description = Resources.FunctionsDirective_Description;
});
public static void Register(IRazorEngineBuilder builder)
public static void Register(RazorProjectEngineBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder.AddDirective(Directive);
builder.Features.Add(new FunctionsDirectivePass());
}
#region Obsolete
public static void Register(IRazorEngineBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder.AddDirective(Directive);
builder.Features.Add(new FunctionsDirectivePass());
}
#endregion
}
}

View File

@ -1,6 +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 Microsoft.AspNetCore.Razor.Language.Legacy;
namespace Microsoft.AspNetCore.Razor.Language.Extensions
@ -17,10 +18,28 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
builder.Description = Resources.InheritsDirective_Description;
});
public static void Register(IRazorEngineBuilder builder)
public static void Register(RazorProjectEngineBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder.AddDirective(Directive);
builder.Features.Add(new InheritsDirectivePass());
}
#region Obsolete
public static void Register(IRazorEngineBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder.AddDirective(Directive);
builder.Features.Add(new InheritsDirectivePass());
}
#endregion
}
}

View File

@ -1,6 +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 Microsoft.AspNetCore.Razor.Language.Legacy;
namespace Microsoft.AspNetCore.Razor.Language.Extensions
@ -16,11 +17,30 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
builder.Description = Resources.SectionDirective_Description;
});
public static void Register(IRazorEngineBuilder builder)
public static void Register(RazorProjectEngineBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder.AddDirective(Directive);
builder.Features.Add(new SectionDirectivePass());
builder.AddTargetExtension(new SectionTargetExtension());
}
#region Obsolete
public static void Register(IRazorEngineBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder.AddDirective(Directive);
builder.Features.Add(new SectionDirectivePass());
builder.AddTargetExtension(new SectionTargetExtension());
}
#endregion
}
}

View File

@ -3,6 +3,7 @@
using System;
using System.Linq;
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
namespace Microsoft.AspNetCore.Razor.Language
{
@ -29,5 +30,77 @@ namespace Microsoft.AspNetCore.Razor.Language
builder.Features.Add(feature);
}
/// <summary>
/// Adds the specified <see cref="ICodeTargetExtension"/>.
/// </summary>
/// <param name="builder">The <see cref="RazorProjectEngineBuilder"/>.</param>
/// <param name="extension">The <see cref="ICodeTargetExtension"/> to add.</param>
/// <returns>The <see cref="RazorProjectEngineBuilder"/>.</returns>
public static RazorProjectEngineBuilder AddTargetExtension(this RazorProjectEngineBuilder builder, ICodeTargetExtension extension)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (extension == null)
{
throw new ArgumentNullException(nameof(extension));
}
var targetExtensionFeature = GetTargetExtensionFeature(builder);
targetExtensionFeature.TargetExtensions.Add(extension);
return builder;
}
/// <summary>
/// Adds the specified <see cref="DirectiveDescriptor"/>.
/// </summary>
/// <param name="builder">The <see cref="RazorProjectEngineBuilder"/>.</param>
/// <param name="directive">The <see cref="DirectiveDescriptor"/> to add.</param>
/// <returns>The <see cref="RazorProjectEngineBuilder"/>.</returns>
public static RazorProjectEngineBuilder AddDirective(this RazorProjectEngineBuilder builder, DirectiveDescriptor directive)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (directive == null)
{
throw new ArgumentNullException(nameof(directive));
}
var directiveFeature = GetDirectiveFeature(builder);
directiveFeature.Directives.Add(directive);
return builder;
}
private static IRazorDirectiveFeature GetDirectiveFeature(RazorProjectEngineBuilder builder)
{
var directiveFeature = builder.Features.OfType<IRazorDirectiveFeature>().FirstOrDefault();
if (directiveFeature == null)
{
directiveFeature = new DefaultRazorDirectiveFeature();
builder.Features.Add(directiveFeature);
}
return directiveFeature;
}
private static IRazorTargetExtensionFeature GetTargetExtensionFeature(RazorProjectEngineBuilder builder)
{
var targetExtensionFeature = builder.Features.OfType<IRazorTargetExtensionFeature>().FirstOrDefault();
if (targetExtensionFeature == null)
{
targetExtensionFeature = new DefaultRazorTargetExtensionFeature();
builder.Features.Add(targetExtensionFeature);
}
return targetExtensionFeature;
}
}
}

View File

@ -0,0 +1,31 @@
// 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;
namespace Microsoft.AspNetCore.Razor.Language
{
public abstract class RazorProjectEngineFeatureBase : IRazorProjectEngineFeature
{
private RazorProjectEngine _projectEngine;
public RazorProjectEngine ProjectEngine
{
get => _projectEngine;
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_projectEngine = value;
OnInitialized();
}
}
protected virtual void OnInitialized()
{
}
}
}

View File

@ -0,0 +1,77 @@
// 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;
using Microsoft.AspNetCore.Razor.Language;
using Moq;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
{
public class DefaultMvcImportFeatureTest
{
[Fact]
public void AddDefaultDirectivesImport_AddsSingleDynamicImport()
{
// Arrange
var imports = new List<RazorSourceDocument>();
// Act
DefaultMvcImportFeature.AddDefaultDirectivesImport(imports);
// Assert
var import = Assert.Single(imports);
Assert.Null(import.FilePath);
}
[Fact]
public void AddHierarchicalImports_AddsViewImportSourceDocumentsOnDisk()
{
// Arrange
var imports = new List<RazorSourceDocument>();
var testFileSystem = new TestRazorProjectFileSystem(new[]
{
new TestRazorProjectItem("/Index.cshtml"),
new TestRazorProjectItem("/_ViewImports.cshtml"),
new TestRazorProjectItem("/Contact/_ViewImports.cshtml"),
new TestRazorProjectItem("/Contact/Index.cshtml"),
});
var mvcImportFeature = new DefaultMvcImportFeature()
{
ProjectEngine = Mock.Of<RazorProjectEngine>(projectEngine => projectEngine.FileSystem == testFileSystem)
};
// Act
mvcImportFeature.AddHierarchicalImports("/Contact/Index.cshtml", imports);
// Assert
Assert.Collection(imports,
import => Assert.Equal("/_ViewImports.cshtml", import.FilePath),
import => Assert.Equal("/Contact/_ViewImports.cshtml", import.FilePath));
}
[Fact]
public void AddHierarchicalImports_AddsViewImportSourceDocumentsNotOnDisk()
{
// Arrange
var imports = new List<RazorSourceDocument>();
var testFileSystem = new TestRazorProjectFileSystem(new[]
{
new TestRazorProjectItem("/Pages/Contact/Index.cshtml"),
});
var mvcImportFeature = new DefaultMvcImportFeature()
{
ProjectEngine = Mock.Of<RazorProjectEngine>(projectEngine => projectEngine.FileSystem == testFileSystem)
};
// Act
mvcImportFeature.AddHierarchicalImports("/Pages/Contact/Index.cshtml", imports);
// Assert
Assert.Collection(imports,
import => Assert.Equal("/_ViewImports.cshtml", import.FilePath),
import => Assert.Equal("/Pages/_ViewImports.cshtml", import.FilePath),
import => Assert.Equal("/Pages/Contact/_ViewImports.cshtml", import.FilePath));
}
}
}

View File

@ -0,0 +1,77 @@
// 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;
using Microsoft.AspNetCore.Razor.Language;
using Moq;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
{
public class DefaultMvcImportFeatureTest
{
[Fact]
public void AddDefaultDirectivesImport_AddsSingleDynamicImport()
{
// Arrange
var imports = new List<RazorSourceDocument>();
// Act
DefaultMvcImportFeature.AddDefaultDirectivesImport(imports);
// Assert
var import = Assert.Single(imports);
Assert.Null(import.FilePath);
}
[Fact]
public void AddHierarchicalImports_AddsViewImportSourceDocumentsOnDisk()
{
// Arrange
var imports = new List<RazorSourceDocument>();
var testFileSystem = new TestRazorProjectFileSystem(new[]
{
new TestRazorProjectItem("/Index.cshtml"),
new TestRazorProjectItem("/_ViewImports.cshtml"),
new TestRazorProjectItem("/Contact/_ViewImports.cshtml"),
new TestRazorProjectItem("/Contact/Index.cshtml"),
});
var mvcImportFeature = new DefaultMvcImportFeature()
{
ProjectEngine = Mock.Of<RazorProjectEngine>(projectEngine => projectEngine.FileSystem == testFileSystem)
};
// Act
mvcImportFeature.AddHierarchicalImports("/Contact/Index.cshtml", imports);
// Assert
Assert.Collection(imports,
import => Assert.Equal("/_ViewImports.cshtml", import.FilePath),
import => Assert.Equal("/Contact/_ViewImports.cshtml", import.FilePath));
}
[Fact]
public void AddHierarchicalImports_AddsViewImportSourceDocumentsNotOnDisk()
{
// Arrange
var imports = new List<RazorSourceDocument>();
var testFileSystem = new TestRazorProjectFileSystem(new[]
{
new TestRazorProjectItem("/Pages/Contact/Index.cshtml"),
});
var mvcImportFeature = new DefaultMvcImportFeature()
{
ProjectEngine = Mock.Of<RazorProjectEngine>(projectEngine => projectEngine.FileSystem == testFileSystem)
};
// Act
mvcImportFeature.AddHierarchicalImports("/Pages/Contact/Index.cshtml", imports);
// Assert
Assert.Collection(imports,
import => Assert.Equal("/_ViewImports.cshtml", import.FilePath),
import => Assert.Equal("/Pages/_ViewImports.cshtml", import.FilePath),
import => Assert.Equal("/Pages/Contact/_ViewImports.cshtml", import.FilePath));
}
}
}

View File

@ -1,6 +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 Microsoft.AspNetCore.Razor.Language.CodeGeneration;
using Moq;
using Xunit;
@ -26,5 +27,77 @@ namespace Microsoft.AspNetCore.Razor.Language
var feature = Assert.Single(builder.Features);
Assert.Same(newFeature, feature);
}
[Fact]
public void AddTargetExtension_CreatesAndAddsToTargetExtensionFeatureIfItDoesNotExist()
{
// Arrange
var builder = new DefaultRazorProjectEngineBuilder(false, Mock.Of<RazorProjectFileSystem>());
var expectedExtension = Mock.Of<ICodeTargetExtension>();
// Act
builder.AddTargetExtension(expectedExtension);
// Assert
var feature = Assert.Single(builder.Features);
var codeTargetExtensionFeature = Assert.IsAssignableFrom<IRazorTargetExtensionFeature>(feature);
var extensions = Assert.Single(codeTargetExtensionFeature.TargetExtensions);
Assert.Same(expectedExtension, extensions);
}
[Fact]
public void AddTargetExtension_UsesExistingFeatureIfExistsAndAddsTo()
{
// Arrange
var builder = new DefaultRazorProjectEngineBuilder(false, Mock.Of<RazorProjectFileSystem>());
var codeTargetExtensionFeature = new DefaultRazorTargetExtensionFeature();
builder.Features.Add(codeTargetExtensionFeature);
var expectedExtension = Mock.Of<ICodeTargetExtension>();
// Act
builder.AddTargetExtension(expectedExtension);
// Assert
var feature = Assert.Single(builder.Features);
Assert.Same(codeTargetExtensionFeature, feature);
var extensions = Assert.Single(codeTargetExtensionFeature.TargetExtensions);
Assert.Same(expectedExtension, extensions);
}
[Fact]
public void AddDirective_CreatesAndAddsToDirectiveFeatureIfItDoesNotExist()
{
// Arrange
var builder = new DefaultRazorProjectEngineBuilder(false, Mock.Of<RazorProjectFileSystem>());
var expectedDirective = Mock.Of<DirectiveDescriptor>();
// Act
builder.AddDirective(expectedDirective);
// Assert
var feature = Assert.Single(builder.Features);
var directiveFeature = Assert.IsAssignableFrom<IRazorDirectiveFeature>(feature);
var directive = Assert.Single(directiveFeature.Directives);
Assert.Same(expectedDirective, directive);
}
[Fact]
public void AddDirective_UsesExistingFeatureIfExistsAndAddsTo()
{
// Arrange
var builder = new DefaultRazorProjectEngineBuilder(false, Mock.Of<RazorProjectFileSystem>());
var directiveFeature = new DefaultRazorDirectiveFeature();
builder.Features.Add(directiveFeature);
var expecteDirective = Mock.Of<DirectiveDescriptor>();
// Act
builder.AddDirective(expecteDirective);
// Assert
var feature = Assert.Single(builder.Features);
Assert.Same(directiveFeature, feature);
var directive = Assert.Single(directiveFeature.Directives);
Assert.Same(expecteDirective, directive);
}
}
}

View File

@ -0,0 +1,34 @@
// 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 Moq;
using Xunit;
namespace Microsoft.AspNetCore.Razor.Language
{
public class RazorProjectEngineFeatureBaseTest
{
[Fact]
public void ProjectEngineSetter_CallsOnInitialized()
{
// Arrange
var testFeature = new TestFeature();
// Act
testFeature.ProjectEngine = Mock.Of<RazorProjectEngine>();
// Assert
Assert.Equal(1, testFeature.InitializationCount);
}
private class TestFeature : RazorProjectEngineFeatureBase
{
public int InitializationCount { get; private set; }
protected override void OnInitialized()
{
InitializationCount++;
}
}
}
}