Add TagHelperComponentTagHelper

Addresses #5728
This commit is contained in:
Jass Bagga 2017-03-29 11:29:24 -07:00 committed by GitHub
parent c18f0780c1
commit eda5028cf4
32 changed files with 696 additions and 34 deletions

View File

@ -52,6 +52,8 @@ namespace Microsoft.AspNetCore.Mvc.Razor
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;

View File

@ -21,6 +21,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
private static readonly Action<ILogger, string, string, Exception> _viewLookupCacheHit;
private static readonly Action<ILogger, string, Exception> _precompiledViewFound;
private static readonly Action<ILogger, string, Exception> _tagHelperComponentInitialized;
private static readonly Action<ILogger, string, Exception> _tagHelperComponentProcessed;
static MvcRazorLoggerExtensions()
{
_razorFileToCodeCompilationStart = LoggerMessage.Define<string>(
@ -57,6 +60,16 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
LogLevel.Debug,
2,
"Compilation of the generated code for the Razor file at '{FilePath}' completed in {ElapsedMilliseconds}ms.");
_tagHelperComponentInitialized = LoggerMessage.Define<string>(
LogLevel.Debug,
2,
"Tag helper component '{ComponentName}' initialized.");
_tagHelperComponentProcessed = LoggerMessage.Define<string>(
LogLevel.Debug,
3,
"Tag helper component '{ComponentName}' processed.");
}
public static void RazorFileToCodeCompilationStart(this ILogger logger, string filePath)
@ -95,6 +108,16 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
_generatedCodeToAssemblyCompilationStart(logger, filePath, null);
}
public static void TagHelperComponentInitialized(this ILogger logger, string componentName)
{
_tagHelperComponentInitialized(logger, componentName, null);
}
public static void TagHelperComponentProcessed(this ILogger logger, string componentName)
{
_tagHelperComponentProcessed(logger, componentName, null);
}
public static void GeneratedCodeToAssemblyCompilationEnd(this ILogger logger, string filePath, long startTimestamp)
{
// Don't log if logging wasn't enabled at start of request as time will be wildly wrong.

View File

@ -0,0 +1,26 @@
// 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.TagHelpers;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Mvc.Razor.TagHelpers
{
/// <summary>
/// A <see cref="TagHelperComponentTagHelper"/> targeting the &lt;body&gt; HTML element.
/// </summary>
[HtmlTargetElement("body")]
public class BodyTagHelper : TagHelperComponentTagHelper
{
/// <summary>
/// Creates a new <see cref="BodyTagHelper"/>.
/// </summary>
/// <param name="components">The list of <see cref="ITagHelperComponent"/>.</param>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
public BodyTagHelper(IEnumerable<ITagHelperComponent> components, ILoggerFactory loggerFactory)
: base(components, loggerFactory)
{
}
}
}

View File

@ -0,0 +1,26 @@
// 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.TagHelpers;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Mvc.Razor.TagHelpers
{
/// <summary>
/// A <see cref="TagHelperComponentTagHelper"/> targeting the &lt;head&gt; HTML element.
/// </summary>
[HtmlTargetElement("head")]
public class HeadTagHelper : TagHelperComponentTagHelper
{
/// <summary>
/// Creates a new <see cref="HeadTagHelper"/>.
/// </summary>
/// <param name="components">The list of <see cref="ITagHelperComponent"/>.</param>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
public HeadTagHelper(IEnumerable<ITagHelperComponent> components, ILoggerFactory loggerFactory)
: base(components, loggerFactory)
{
}
}
}

View File

@ -0,0 +1,69 @@
// 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.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Razor.Internal;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Mvc.Razor.TagHelpers
{
public abstract class TagHelperComponentTagHelper : TagHelper
{
private readonly ILogger _logger;
private IEnumerable<ITagHelperComponent> _components;
/// <summary>
/// Creates a new <see cref="TagHelperComponentTagHelper"/>.
/// </summary>
/// <param name="components">The list of <see cref="ITagHelperComponent"/>.</param>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
public TagHelperComponentTagHelper(IEnumerable<ITagHelperComponent> components,
ILoggerFactory loggerFactory)
{
if (components == null)
{
throw new ArgumentNullException(nameof(components));
}
if (loggerFactory == null)
{
throw new ArgumentNullException(nameof(loggerFactory));
}
_components = components;
_logger = loggerFactory.CreateLogger(GetType());
}
/// <inheritdoc />
public override void Init(TagHelperContext context)
{
_components = _components.OrderBy(p => p.Order);
foreach (var component in _components)
{
component.Init(context);
if (_logger.IsEnabled(LogLevel.Debug))
{
_logger.TagHelperComponentInitialized(component.GetType().FullName);
}
}
}
/// <inheritdoc />
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
foreach (var component in _components)
{
await component.ProcessAsync(context, output);
if (_logger.IsEnabled(LogLevel.Debug))
{
_logger.TagHelperComponentProcessed(component.GetType().FullName);
}
}
}
}
}

View File

@ -0,0 +1,71 @@
// 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.Net;
using System.Net.Http;
using System.Reflection;
using System.Threading.Tasks;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public class TagHelperComponentTagHelperTest : IClassFixture<MvcTestFixture<RazorWebSite.Startup>>
{
private static readonly Assembly _resourcesAssembly = typeof(TagHelperComponentTagHelperTest).GetTypeInfo().Assembly;
public TagHelperComponentTagHelperTest(MvcTestFixture<RazorWebSite.Startup> fixture)
{
Client = fixture.Client;
}
public HttpClient Client { get; }
[Fact]
public async Task InjectsTestHeadTagHelperComponent()
{
// Arrange
var url = "http://localhost/TagHelperComponent/GetHead";
var request = new HttpRequestMessage(HttpMethod.Get, url);
var outputFile = "compiler/resources/RazorWebSite.TagHelperComponent.Head.html";
var expectedContent =
await ResourceFile.ReadResourceAsync(_resourcesAssembly, outputFile, sourceFile: false);
// Act
var response = await Client.SendAsync(request);
var responseContent = await response.Content.ReadAsStringAsync();
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
#if GENERATE_BASELINES
ResourceFile.UpdateFile(_resourcesAssembly, outputFile, expectedContent, responseContent);
#else
Assert.Equal(expectedContent, responseContent, ignoreLineEndingDifferences: true);
#endif
}
[Fact]
public async Task InjectsTestBodyTagHelperComponent()
{
// Arrange
var url = "http://localhost/TagHelperComponent/GetBody";
var request = new HttpRequestMessage(HttpMethod.Get, url);
var outputFile = "compiler/resources/RazorWebSite.TagHelperComponent.Body.html";
var expectedContent =
await ResourceFile.ReadResourceAsync(_resourcesAssembly, outputFile, sourceFile: false);
// Act
var response = await Client.SendAsync(request);
var responseContent = await response.Content.ReadAsStringAsync();
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
#if GENERATE_BASELINES
ResourceFile.UpdateFile(_resourcesAssembly, outputFile, expectedContent, responseContent);
#else
Assert.Equal(expectedContent, responseContent, ignoreLineEndingDifferences: true);
#endif
}
}
}

View File

@ -0,0 +1,3 @@
<body inject="true">
Hello from Body Tag Helper Component
<script>'This was injected!!'</script></body>

View File

@ -0,0 +1,3 @@
<head inject="true">
Hello from Head Tag Helper Component
<script>'This was injected!!'</script></head>

View File

@ -81,9 +81,13 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Host
var importContent = GetContent(imports)
.Split(new[] { Environment.NewLine }, StringSplitOptions.None)
.Where(line => line.StartsWith("@addTagHelper"));
var addTagHelper = Assert.Single(importContent);
Assert.Equal("@addTagHelper Microsoft.AspNetCore.Mvc.Razor.TagHelpers.UrlResolutionTagHelper, Microsoft.AspNetCore.Mvc.Razor",
addTagHelper);
Assert.Contains("@addTagHelper Microsoft.AspNetCore.Mvc.Razor.TagHelpers.UrlResolutionTagHelper, Microsoft.AspNetCore.Mvc.Razor",
importContent);
Assert.Contains("@addTagHelper Microsoft.AspNetCore.Mvc.Razor.TagHelpers.HeadTagHelper, Microsoft.AspNetCore.Mvc.Razor",
importContent);
Assert.Contains("@addTagHelper Microsoft.AspNetCore.Mvc.Razor.TagHelpers.BodyTagHelper, Microsoft.AspNetCore.Mvc.Razor",
importContent);
}
[Fact]

View File

@ -74,6 +74,14 @@ System.Object ModelExpressionProvider = null;
))();
((System.Action)(() => {
System.Object __typeHelper = "Microsoft.AspNetCore.Mvc.Razor.TagHelpers.UrlResolutionTagHelper, Microsoft.AspNetCore.Mvc.Razor";
}
))();
((System.Action)(() => {
System.Object __typeHelper = "Microsoft.AspNetCore.Mvc.Razor.TagHelpers.HeadTagHelper, Microsoft.AspNetCore.Mvc.Razor";
}
))();
((System.Action)(() => {
System.Object __typeHelper = "Microsoft.AspNetCore.Mvc.Razor.TagHelpers.BodyTagHelper, Microsoft.AspNetCore.Mvc.Razor";
}
))();
}

View File

@ -1,10 +1,10 @@
Source Location: (13:0,13 [4] TestFiles/Input/Basic.cshtml)
|logo|
Generated Location: (2329:85,13 [4] )
Generated Location: (2691:93,13 [4] )
|logo|
Source Location: (43:2,5 [21] TestFiles/Input/Basic.cshtml)
|Html.Input("SomeKey")|
Generated Location: (2413:90,6 [21] )
Generated Location: (2775:98,6 [21] )
|Html.Input("SomeKey")|

View File

@ -82,6 +82,14 @@ System.Object __typeHelper = "Microsoft.AspNetCore.Mvc.Razor.TagHelpers.UrlResol
}
))();
((System.Action)(() => {
System.Object __typeHelper = "Microsoft.AspNetCore.Mvc.Razor.TagHelpers.HeadTagHelper, Microsoft.AspNetCore.Mvc.Razor";
}
))();
((System.Action)(() => {
System.Object __typeHelper = "Microsoft.AspNetCore.Mvc.Razor.TagHelpers.BodyTagHelper, Microsoft.AspNetCore.Mvc.Razor";
}
))();
((System.Action)(() => {
MyApp __typeHelper = null;
}
))();

View File

@ -1,10 +1,10 @@
Source Location: (28:1,8 [5] TestFiles/Input/Inject.cshtml)
|MyApp|
Generated Location: (2174:84,0 [5] )
Generated Location: (2536:92,0 [5] )
|MyApp|
Source Location: (34:1,14 [14] TestFiles/Input/Inject.cshtml)
|MyPropertyName|
Generated Location: (2276:88,14 [14] )
Generated Location: (2638:96,14 [14] )
|MyPropertyName|

View File

@ -77,6 +77,14 @@ System.Object __typeHelper = "Microsoft.AspNetCore.Mvc.Razor.TagHelpers.UrlResol
}
))();
((System.Action)(() => {
System.Object __typeHelper = "Microsoft.AspNetCore.Mvc.Razor.TagHelpers.HeadTagHelper, Microsoft.AspNetCore.Mvc.Razor";
}
))();
((System.Action)(() => {
System.Object __typeHelper = "Microsoft.AspNetCore.Mvc.Razor.TagHelpers.BodyTagHelper, Microsoft.AspNetCore.Mvc.Razor";
}
))();
((System.Action)(() => {
MyModel __typeHelper = null;
}
))();

View File

@ -1,25 +1,25 @@
Source Location: (7:0,7 [7] TestFiles/Input/InjectWithModel.cshtml)
|MyModel|
Generated Location: (2091:79,0 [7] )
Generated Location: (2453:87,0 [7] )
|MyModel|
Source Location: (24:1,8 [5] TestFiles/Input/InjectWithModel.cshtml)
|MyApp|
Generated Location: (2181:83,0 [5] )
Generated Location: (2543:91,0 [5] )
|MyApp|
Source Location: (30:1,14 [14] TestFiles/Input/InjectWithModel.cshtml)
|MyPropertyName|
Generated Location: (2283:87,14 [14] )
Generated Location: (2645:95,14 [14] )
|MyPropertyName|
Source Location: (54:2,8 [17] TestFiles/Input/InjectWithModel.cshtml)
|MyService<TModel>|
Generated Location: (2367:91,0 [17] )
Generated Location: (2729:99,0 [17] )
|MyService<TModel>|
Source Location: (72:2,26 [4] TestFiles/Input/InjectWithModel.cshtml)
|Html|
Generated Location: (2481:95,14 [4] )
Generated Location: (2843:103,14 [4] )
|Html|

View File

@ -77,6 +77,14 @@ System.Object __typeHelper = "Microsoft.AspNetCore.Mvc.Razor.TagHelpers.UrlResol
}
))();
((System.Action)(() => {
System.Object __typeHelper = "Microsoft.AspNetCore.Mvc.Razor.TagHelpers.HeadTagHelper, Microsoft.AspNetCore.Mvc.Razor";
}
))();
((System.Action)(() => {
System.Object __typeHelper = "Microsoft.AspNetCore.Mvc.Razor.TagHelpers.BodyTagHelper, Microsoft.AspNetCore.Mvc.Razor";
}
))();
((System.Action)(() => {
MyModel __typeHelper = null;
}
))();

View File

@ -1,45 +1,45 @@
Source Location: (7:0,7 [7] TestFiles/Input/InjectWithSemicolon.cshtml)
|MyModel|
Generated Location: (2095:79,0 [7] )
Generated Location: (2457:87,0 [7] )
|MyModel|
Source Location: (24:1,8 [5] TestFiles/Input/InjectWithSemicolon.cshtml)
|MyApp|
Generated Location: (2185:83,0 [5] )
Generated Location: (2547:91,0 [5] )
|MyApp|
Source Location: (30:1,14 [14] TestFiles/Input/InjectWithSemicolon.cshtml)
|MyPropertyName|
Generated Location: (2287:87,14 [14] )
Generated Location: (2649:95,14 [14] )
|MyPropertyName|
Source Location: (58:2,8 [17] TestFiles/Input/InjectWithSemicolon.cshtml)
|MyService<TModel>|
Generated Location: (2371:91,0 [17] )
Generated Location: (2733:99,0 [17] )
|MyService<TModel>|
Source Location: (76:2,26 [4] TestFiles/Input/InjectWithSemicolon.cshtml)
|Html|
Generated Location: (2485:95,14 [4] )
Generated Location: (2847:103,14 [4] )
|Html|
Source Location: (93:3,8 [5] TestFiles/Input/InjectWithSemicolon.cshtml)
|MyApp|
Generated Location: (2559:99,0 [5] )
Generated Location: (2921:107,0 [5] )
|MyApp|
Source Location: (99:3,14 [15] TestFiles/Input/InjectWithSemicolon.cshtml)
|MyPropertyName2|
Generated Location: (2661:103,14 [15] )
Generated Location: (3023:111,14 [15] )
|MyPropertyName2|
Source Location: (129:4,8 [17] TestFiles/Input/InjectWithSemicolon.cshtml)
|MyService<TModel>|
Generated Location: (2746:107,0 [17] )
Generated Location: (3108:115,0 [17] )
|MyService<TModel>|
Source Location: (147:4,26 [5] TestFiles/Input/InjectWithSemicolon.cshtml)
|Html2|
Generated Location: (2860:111,14 [5] )
Generated Location: (3222:119,14 [5] )
|Html2|

View File

@ -77,6 +77,14 @@ System.Object __typeHelper = "Microsoft.AspNetCore.Mvc.Razor.TagHelpers.UrlResol
}
))();
((System.Action)(() => {
System.Object __typeHelper = "Microsoft.AspNetCore.Mvc.Razor.TagHelpers.HeadTagHelper, Microsoft.AspNetCore.Mvc.Razor";
}
))();
((System.Action)(() => {
System.Object __typeHelper = "Microsoft.AspNetCore.Mvc.Razor.TagHelpers.BodyTagHelper, Microsoft.AspNetCore.Mvc.Razor";
}
))();
((System.Action)(() => {
System.Collections.IEnumerable __typeHelper = null;
}
))();

View File

@ -1,5 +1,5 @@
Source Location: (7:0,7 [30] TestFiles/Input/Model.cshtml)
|System.Collections.IEnumerable|
Generated Location: (2104:79,0 [30] )
Generated Location: (2466:87,0 [30] )
|System.Collections.IEnumerable|

View File

@ -77,6 +77,14 @@ System.Object __typeHelper = "Microsoft.AspNetCore.Mvc.Razor.TagHelpers.UrlResol
}
))();
((System.Action)(() => {
System.Object __typeHelper = "Microsoft.AspNetCore.Mvc.Razor.TagHelpers.HeadTagHelper, Microsoft.AspNetCore.Mvc.Razor";
}
))();
((System.Action)(() => {
System.Object __typeHelper = "Microsoft.AspNetCore.Mvc.Razor.TagHelpers.BodyTagHelper, Microsoft.AspNetCore.Mvc.Razor";
}
))();
((System.Action)(() => {
DateTime __typeHelper = null;
}
))();

View File

@ -1,40 +1,40 @@
Source Location: (7:0,7 [8] TestFiles/Input/ModelExpressionTagHelper.cshtml)
|DateTime|
Generated Location: (2101:79,0 [8] )
Generated Location: (2463:87,0 [8] )
|DateTime|
Source Location: (33:2,14 [91] TestFiles/Input/ModelExpressionTagHelper.cshtml)
|Microsoft.AspNetCore.Mvc.Razor.InputTestTagHelper, Microsoft.AspNetCore.Mvc.Razor.Host.Test|
Generated Location: (2222:83,30 [91] )
Generated Location: (2584:91,30 [91] )
|Microsoft.AspNetCore.Mvc.Razor.InputTestTagHelper, Microsoft.AspNetCore.Mvc.Razor.Host.Test|
Source Location: (140:3,14 [102] TestFiles/Input/ModelExpressionTagHelper.cshtml)
|Microsoft.AspNetCore.Mvc.Razor.DictionaryPrefixTestTagHelper, Microsoft.AspNetCore.Mvc.Razor.Host.Test|
Generated Location: (2407:87,30 [102] )
Generated Location: (2769:95,30 [102] )
|Microsoft.AspNetCore.Mvc.Razor.DictionaryPrefixTestTagHelper, Microsoft.AspNetCore.Mvc.Razor.Host.Test|
Source Location: (263:5,17 [3] TestFiles/Input/ModelExpressionTagHelper.cshtml)
|Now|
Generated Location: (3397:100,133 [3] )
Generated Location: (3759:108,133 [3] )
|Now|
Source Location: (290:6,18 [5] TestFiles/Input/ModelExpressionTagHelper.cshtml)
|Model|
Generated Location: (3764:106,125 [5] )
Generated Location: (4126:114,125 [5] )
|Model|
Source Location: (322:8,19 [5] TestFiles/Input/ModelExpressionTagHelper.cshtml)
|Model|
Generated Location: (4183:112,153 [5] )
Generated Location: (4545:120,153 [5] )
|Model|
Source Location: (357:9,19 [4] TestFiles/Input/ModelExpressionTagHelper.cshtml)
|Hour|
Generated Location: (4611:118,161 [4] )
Generated Location: (4973:126,161 [4] )
|Hour|
Source Location: (378:9,40 [6] TestFiles/Input/ModelExpressionTagHelper.cshtml)
|Minute|
Generated Location: (4873:123,163 [6] )
Generated Location: (5235:131,163 [6] )
|Minute|

View File

@ -77,6 +77,14 @@ System.Object __typeHelper = "Microsoft.AspNetCore.Mvc.Razor.TagHelpers.UrlResol
}
))();
((System.Action)(() => {
System.Object __typeHelper = "Microsoft.AspNetCore.Mvc.Razor.TagHelpers.HeadTagHelper, Microsoft.AspNetCore.Mvc.Razor";
}
))();
((System.Action)(() => {
System.Object __typeHelper = "Microsoft.AspNetCore.Mvc.Razor.TagHelpers.BodyTagHelper, Microsoft.AspNetCore.Mvc.Razor";
}
))();
((System.Action)(() => {
ThisShouldBeGenerated __typeHelper = null;
}
))();

View File

@ -1,10 +1,10 @@
Source Location: (7:0,7 [21] TestFiles/Input/MultipleModels.cshtml)
|ThisShouldBeGenerated|
Generated Location: (2113:79,0 [21] )
Generated Location: (2475:87,0 [21] )
|ThisShouldBeGenerated|
Source Location: (37:1,7 [30] TestFiles/Input/MultipleModels.cshtml)
|System.Collections.IEnumerable|
Generated Location: (2217:83,0 [30] )
Generated Location: (2579:91,0 [30] )
|System.Collections.IEnumerable|

View File

@ -77,6 +77,14 @@ System.Object __typeHelper = "Microsoft.AspNetCore.Mvc.Razor.TagHelpers.UrlResol
}
))();
((System.Action)(() => {
System.Object __typeHelper = "Microsoft.AspNetCore.Mvc.Razor.TagHelpers.HeadTagHelper, Microsoft.AspNetCore.Mvc.Razor";
}
))();
((System.Action)(() => {
System.Object __typeHelper = "Microsoft.AspNetCore.Mvc.Razor.TagHelpers.BodyTagHelper, Microsoft.AspNetCore.Mvc.Razor";
}
))();
((System.Action)(() => {
IHtmlHelper<TModel> __typeHelper = null;
}
))();

View File

@ -1,10 +1,10 @@
Source Location: (8:0,8 [19] TestFiles/Input/_ViewImports.cshtml)
|IHtmlHelper<TModel>|
Generated Location: (2088:79,0 [19] )
Generated Location: (2450:87,0 [19] )
|IHtmlHelper<TModel>|
Source Location: (28:0,28 [5] TestFiles/Input/_ViewImports.cshtml)
|Model|
Generated Location: (2204:83,14 [5] )
Generated Location: (2566:91,14 [5] )
|Model|

View File

@ -0,0 +1,292 @@
// 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.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging.Testing;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.Razor.TagHelpers
{
public class TagHelperComponentTagHelperTest
{
[Fact]
public void Init_InvokesComponentsInitInCorrectOrder()
{
// Arrange
var tagHelperContext = new TagHelperContext(
"head",
allAttributes: new TagHelperAttributeList(
Enumerable.Empty<TagHelperAttribute>()),
items: new Dictionary<object, object>(),
uniqueId: "test");
var incrementer = 0;
var testTagHelperComponentTagHelper = new TestTagHelperComponentTagHelper(new []
{
new CallbackTagHelperComponent(
order: 2,
initCallback: () =>
{
Assert.Equal(1, incrementer);
incrementer++;
},
processAsyncCallback: null),
new CallbackTagHelperComponent(
order: 3,
initCallback: () =>
{
Assert.Equal(2, incrementer);
incrementer++;
},
processAsyncCallback: null),
new CallbackTagHelperComponent(
order: 1,
initCallback: () =>
{
Assert.Equal(0, incrementer);
incrementer++;
},
processAsyncCallback: null),
}, NullLoggerFactory.Instance);
// Act
testTagHelperComponentTagHelper.Init(tagHelperContext);
// Assert
Assert.Equal(3, incrementer);
}
[Fact]
public async void ProcessAsync_InvokesComponentsProcessAsyncInCorrectOrder()
{
// Arrange
var tagHelperContext = new TagHelperContext(
"head",
allAttributes: new TagHelperAttributeList(
Enumerable.Empty<TagHelperAttribute>()),
items: new Dictionary<object, object>(),
uniqueId: "test");
var output = new TagHelperOutput(
"head",
attributes: new TagHelperAttributeList(),
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()));
var incrementer = 0;
var testTagHelperComponentTagHelper = new TestTagHelperComponentTagHelper(new []
{
new CallbackTagHelperComponent(
order: 2,
initCallback: () => { },
processAsyncCallback: () =>
{
Assert.Equal(1, incrementer);
incrementer++;
}),
new CallbackTagHelperComponent(
order: 3,
initCallback: () => { },
processAsyncCallback: () =>
{
Assert.Equal(2, incrementer);
incrementer++;
}),
new CallbackTagHelperComponent(
order: 1,
initCallback: () => { },
processAsyncCallback: () =>
{
Assert.Equal(0, incrementer);
incrementer++;
}),
}, NullLoggerFactory.Instance);
// Act
testTagHelperComponentTagHelper.Init(tagHelperContext);
await testTagHelperComponentTagHelper.ProcessAsync(tagHelperContext, output);
// Assert
Assert.Equal(3, incrementer);
}
[Fact]
public void Init_InvokesTagHelperComponentInit()
{
// Arrange
var tagHelperContext = new TagHelperContext(
"head",
allAttributes: new TagHelperAttributeList(
Enumerable.Empty<TagHelperAttribute>()),
items: new Dictionary<object, object>(),
uniqueId: "test");
var output = new TagHelperOutput(
"head",
attributes: new TagHelperAttributeList(),
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()));
var testTagHelperComponentTagHelper = new TestTagHelperComponentTagHelper(new []
{
new TestTagHelperComponent()
}, NullLoggerFactory.Instance);
// Act
testTagHelperComponentTagHelper.Init(tagHelperContext);
// Assert
Assert.Equal("Value", tagHelperContext.Items["Key"]);
}
[Fact]
public async Task ProcessAsync_InvokesTagHelperComponentProcessAsync()
{
// Arrange
var tagHelperContext = new TagHelperContext(
"head",
allAttributes: new TagHelperAttributeList(
Enumerable.Empty<TagHelperAttribute>()),
items: new Dictionary<object, object>(),
uniqueId: "test");
var output = new TagHelperOutput(
"head",
attributes: new TagHelperAttributeList(),
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()));
var testTagHelperComponentTagHelper = new TestTagHelperComponentTagHelper(new []
{
new TestTagHelperComponent()
}, NullLoggerFactory.Instance);
// Act
await testTagHelperComponentTagHelper.ProcessAsync(tagHelperContext, output);
// Assert
Assert.Equal("Processed", output.PostContent.GetContent());
}
[Fact]
public void Init_LogsTagHelperComponentInitialized()
{
// Arrange
var sink = new TestSink();
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
var tagHelperContext = new TagHelperContext(
"head",
allAttributes: new TagHelperAttributeList(
Enumerable.Empty<TagHelperAttribute>()),
items: new Dictionary<object, object>(),
uniqueId: "test");
var output = new TagHelperOutput(
"head",
attributes: new TagHelperAttributeList(),
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()));
var testTagHelperComponentTagHelper = new TestTagHelperComponentTagHelper(new []
{
new TestTagHelperComponent()
}, loggerFactory);
// Act
testTagHelperComponentTagHelper.Init(tagHelperContext);
// Assert
Assert.Equal($"Tag helper component '{typeof(TestTagHelperComponent)}' initialized.", sink.Writes[0].State.ToString(), StringComparer.Ordinal);
}
[Fact]
public async Task ProcessAsync_LogsTagHelperComponentProcessed()
{
// Arrange
var sink = new TestSink();
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
var tagHelperContext = new TagHelperContext(
"head",
allAttributes: new TagHelperAttributeList(
Enumerable.Empty<TagHelperAttribute>()),
items: new Dictionary<object, object>(),
uniqueId: "test");
var output = new TagHelperOutput(
"head",
attributes: new TagHelperAttributeList(),
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()));
var testTagHelperComponentTagHelper = new TestTagHelperComponentTagHelper(new []
{
new TestTagHelperComponent()
}, loggerFactory);
// Act
await testTagHelperComponentTagHelper.ProcessAsync(tagHelperContext, output);
// Assert
Assert.Equal($"Tag helper component '{typeof(TestTagHelperComponent)}' processed.", sink.Writes[0].State.ToString(), StringComparer.Ordinal);
}
private class TestTagHelperComponentTagHelper : TagHelperComponentTagHelper
{
public TestTagHelperComponentTagHelper(
IEnumerable<ITagHelperComponent> components,
ILoggerFactory loggerFactory)
: base(components, loggerFactory)
{
}
}
private class CallbackTagHelperComponent : ITagHelperComponent
{
private readonly Action _initCallback;
private readonly Action _processAsyncCallback;
private readonly int _order;
public CallbackTagHelperComponent(int order, Action initCallback, Action processAsyncCallback)
{
_initCallback = initCallback;
_processAsyncCallback = processAsyncCallback;
_order = order;
}
public int Order => _order;
public void Init(TagHelperContext context)
{
_initCallback();
}
public Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
_processAsyncCallback();
return Task.CompletedTask;
}
}
private class TestTagHelperComponent : ITagHelperComponent
{
public int Order => 1;
public void Init(TagHelperContext context)
{
context.Items["Key"] = "Value";
}
public Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
output.PostContent.AppendHtml("Processed");
return Task.CompletedTask;
}
}
}
}

View File

@ -0,0 +1,21 @@
// 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.Mvc;
namespace RazorWebSite
{
public class TagHelperComponentController : Controller
{
// GET: /<controller>/
public IActionResult GetHead()
{
return View("Head");
}
public IActionResult GetBody()
{
return View("Body");
}
}
}

View File

@ -0,0 +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.Threading.Tasks;
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace RazorWebSite
{
public class TestBodyTagHelperComponent : TagHelperComponent
{
public override int Order => 1;
public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
if (string.Equals(context.TagName, "body", StringComparison.Ordinal) && output.Attributes.ContainsName("inject"))
{
output.PostContent.AppendHtml("<script>'This was injected!!'</script>");
}
return Task.FromResult(0);
}
}
}

View File

@ -0,0 +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.Threading.Tasks;
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace RazorWebSite
{
public class TestHeadTagHelperComponent : TagHelperComponent
{
public override int Order => 1;
public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
if (string.Equals(context.TagName, "head", StringComparison.Ordinal) && output.Attributes.ContainsName("inject"))
{
output.PostContent.AppendHtml("<script>'This was injected!!'</script>");
}
return Task.FromResult(0);
}
}
}

View File

@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
@ -20,6 +21,9 @@ namespace RazorWebSite
{
var updateableFileProvider = new UpdateableFileProvider();
services.AddSingleton(updateableFileProvider);
services.AddSingleton<ITagHelperComponent, TestHeadTagHelperComponent>();
services.AddSingleton<ITagHelperComponent, TestBodyTagHelperComponent>();
services
.AddMvc()
.AddRazorOptions(options =>

View File

@ -0,0 +1,3 @@
<body inject="true">
Hello from Body Tag Helper Component
</body>

View File

@ -0,0 +1,3 @@
<head inject="true">
Hello from Head Tag Helper Component
</head>