Unit tests for VCTH and model directive

This commit is contained in:
Ryan Nowak 2017-01-31 12:17:44 -08:00
parent 3ade209bf0
commit 4c62eb89af
8 changed files with 566 additions and 269 deletions

View File

@ -49,7 +49,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Host
return "dynamic";
}
private class Pass : IRazorIRPass
internal class Pass : IRazorIRPass
{
public RazorEngine Engine { get; set; }

View File

@ -147,7 +147,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Host
writer.WriteAutoPropertyDeclaration(
"public",
$"global::Microsoft.AspNetCore.Mvc.Rendering.ViewContext",
"_context");
"ViewContext");
var indexerAttributes = descriptor.Attributes.Where(a => a.IsIndexer);
@ -187,19 +187,19 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Host
writer.WriteInstanceMethodInvocation(
$"(_helper as global::Microsoft.AspNetCore.Mvc.ViewFeatures.IViewContextAware)?",
"Contextualize",
new[] { "_context" });
new[] { "ViewContext" });
var methodParameters = GetMethodParameters(descriptor);
var viewContentVariable = "viewContent";
var contentVariable = "content";
writer.Write("var ")
.WriteStartAssignment(viewContentVariable)
.WriteStartAssignment(contentVariable)
.WriteInstanceMethodInvocation($"await _helper", "InvokeAsync", methodParameters);
writer.WriteStartAssignment($"{outputVariable}.TagName")
.WriteLine("null;");
writer.WriteInstanceMethodInvocation(
$"{outputVariable}.Content",
"SetHtmlContent",
new[] { viewContentVariable });
new[] { contentVariable });
}
}
@ -207,7 +207,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Host
{
var propertyNames = descriptor.Attributes.Where(a => !a.IsIndexer).Select(attribute => attribute.PropertyName);
var joinedPropertyNames = string.Join(", ", propertyNames);
var parametersString = $" new {{ { joinedPropertyNames } }}";
var parametersString = $"new {{ { joinedPropertyNames } }}";
var viewComponentName = descriptor.PropertyBag[
ViewComponentTagHelperDescriptorConventions.ViewComponentNameKey];

View File

@ -1,94 +0,0 @@
// 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.Chunks;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.Razor.Directives
{
public class SetBaseTypeChunkMergerTest
{
#if OLD_RAZOR
[Theory]
[InlineData("MyApp.BaseType<TModel>", "MyApp.BaseType<Person>")]
[InlineData("TestBaseType", "TestBaseType")]
public void Visit_UpdatesTModelTokenToMatchModelType(string typeName, string expectedValue)
{
// Arrange
var chunk = new SetBaseTypeChunk
{
TypeName = typeName,
};
var merger = new SetBaseTypeChunkMerger("Person");
// Act
merger.VisitChunk(chunk);
// Assert
Assert.Equal(expectedValue, chunk.TypeName);
}
[Fact]
public void Merge_SetsBaseTypeIfItHasNotBeenSetInChunkTree()
{
// Arrange
var expected = "MyApp.Razor.MyBaseType";
var merger = new SetBaseTypeChunkMerger("dynamic");
var chunkTree = new ChunkTree();
var inheritedChunks = new[]
{
new SetBaseTypeChunk { TypeName = expected }
};
// Act
merger.MergeInheritedChunks(chunkTree, inheritedChunks);
// Assert
var chunk = Assert.Single(chunkTree.Children);
var setBaseTypeChunk = Assert.IsType<SetBaseTypeChunk>(chunk);
Assert.Equal(expected, setBaseTypeChunk.TypeName);
}
[Fact]
public void Merge_IgnoresSetBaseTypeChunksIfChunkTreeContainsOne()
{
// Arrange
var merger = new SetBaseTypeChunkMerger("dynamic");
var chunkTree = new ChunkTree();
var inheritedChunks = new[]
{
new SetBaseTypeChunk { TypeName = "MyBaseType2" }
};
// Act
merger.VisitChunk(new SetBaseTypeChunk { TypeName = "MyBaseType1" });
merger.MergeInheritedChunks(chunkTree, inheritedChunks);
// Assert
Assert.Empty(chunkTree.Children);
}
[Fact]
public void Merge_PicksLastBaseTypeChunkFromChunkTree()
{
// Arrange
var merger = new SetBaseTypeChunkMerger("dynamic");
var chunkTree = new ChunkTree();
var inheritedChunks = new Chunk[]
{
new SetBaseTypeChunk { TypeName = "MyBase2" },
new LiteralChunk(),
new SetBaseTypeChunk { TypeName = "MyBase1" },
};
// Act
merger.MergeInheritedChunks(chunkTree, inheritedChunks);
// Assert
var chunk = Assert.Single(chunkTree.Children);
var setBaseTypeChunk = Assert.IsType<SetBaseTypeChunk>(chunk);
Assert.Equal("MyBase1", setBaseTypeChunk.TypeName);
}
#endif
}
}

View File

@ -1,61 +0,0 @@
// 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 System.Reflection;
using Microsoft.AspNetCore.Razor.Chunks;
using Microsoft.AspNetCore.Razor.CodeGenerators;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.Razor.Host.Test.Internal
{
public class ViewComponentTagHelperChunkVisitorTest
{
#if OLD_RAZOR
public static TheoryData CodeGenerationData
{
get
{
var oneInstanceChunks = ChunkVisitorTestFactory.GetTestChunks(visitedTagHelperChunks: true);
var twoInstanceChunks = ChunkVisitorTestFactory.GetTestChunks(visitedTagHelperChunks: true);
twoInstanceChunks.Add(twoInstanceChunks[twoInstanceChunks.Count - 1]);
return new TheoryData<IList<Chunk>>
{
oneInstanceChunks,
twoInstanceChunks
};
}
}
[Theory]
[MemberData(nameof(CodeGenerationData))]
public void Accept_CorrectlyGeneratesCode(IList<Chunk> chunks)
{
// Arrange
var writer = new CSharpCodeWriter();
var context = ChunkVisitorTestFactory.CreateCodeGeneratorContext();
var chunkVisitor = new ViewComponentTagHelperChunkVisitor(writer, context);
var assembly = typeof(ViewComponentTagHelperChunkVisitorTest).GetTypeInfo().Assembly;
var path = "TestFiles/Output/Runtime/GeneratedViewComponentTagHelperClasses.cs";
var expectedOutput = ResourceFile.ReadResource(assembly, path, sourceFile: true);
// Act
chunkVisitor.Accept(chunks);
var resultOutput = writer.GenerateCode();
#if GENERATE_BASELINES
if (!string.Equals(expectedOutput, resultOutput, System.StringComparison.Ordinal))
{
ResourceFile.UpdateFile(assembly, path, expectedOutput, resultOutput);
}
#else
// Assert
Assert.Equal(expectedOutput, resultOutput, ignoreLineEndingDifferences: true);
#endif
}
#endif
}
}

View File

@ -0,0 +1,223 @@
// 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;
using Microsoft.AspNetCore.Razor.Evolution;
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.Razor.Host
{
public class ModelDirectiveTest
{
[Fact]
public void ModelDirective_GetModelType_GetsTypeFromLastWellFormedDirective()
{
// Arrange
var codeDocument = CreateDocument(@"
@model Type1
@model Type2
@model
");
var engine = CreateEngine();
var irDocument = CreateIRDocument(engine, codeDocument);
// Act
var result = ModelDirective.GetModelType(irDocument);
// Assert
Assert.Equal("Type2", result);
}
[Fact]
public void ModelDirective_GetModelType_DefaultsToDynamic()
{
// Arrange
var codeDocument = CreateDocument(@" ");
var engine = CreateEngine();
var irDocument = CreateIRDocument(engine, codeDocument);
// Act
var result = ModelDirective.GetModelType(irDocument);
// Assert
Assert.Equal("dynamic", result);
}
[Fact]
public void ModelDirectivePass_Execute_ReplacesTModelInBaseType()
{
// Arrange
var codeDocument = CreateDocument(@"
@inherits BaseType<TModel>
@model Type1
");
var engine = CreateEngine();
var pass = new ModelDirective.Pass()
{
Engine = engine,
};
var irDocument = CreateIRDocument(engine, codeDocument);
// Act
pass.Execute(codeDocument, irDocument);
// Assert
var @class = FindClassNode(irDocument);
Assert.NotNull(@class);
Assert.Equal("BaseType<Type1>", @class.BaseType);
}
[Fact]
public void ModelDirectivePass_Execute_ReplacesTModelInBaseType_DifferentOrdering()
{
// Arrange
var codeDocument = CreateDocument(@"
@model Type1
@inherits BaseType<TModel>
@model Type2
");
var engine = CreateEngine();
var pass = new ModelDirective.Pass()
{
Engine = engine,
};
var irDocument = CreateIRDocument(engine, codeDocument);
// Act
pass.Execute(codeDocument, irDocument);
// Assert
var @class = FindClassNode(irDocument);
Assert.NotNull(@class);
Assert.Equal("BaseType<Type2>", @class.BaseType);
}
[Fact]
public void ModelDirectivePass_Execute_NoOpWithoutTModel()
{
// Arrange
var codeDocument = CreateDocument(@"
@inherits BaseType
@model Type1
");
var engine = CreateEngine();
var pass = new ModelDirective.Pass()
{
Engine = engine,
};
var irDocument = CreateIRDocument(engine, codeDocument);
// Act
pass.Execute(codeDocument, irDocument);
// Assert
var @class = FindClassNode(irDocument);
Assert.NotNull(@class);
Assert.Equal("BaseType", @class.BaseType);
}
[Fact]
public void ModelDirectivePass_Execute_ReplacesTModelInBaseType_DefaultDynamic()
{
// Arrange
var codeDocument = CreateDocument(@"
@inherits BaseType<TModel>
");
var engine = CreateEngine();
var pass = new ModelDirective.Pass()
{
Engine = engine,
};
var irDocument = CreateIRDocument(engine, codeDocument);
// Act
pass.Execute(codeDocument, irDocument);
// Assert
var @class = FindClassNode(irDocument);
Assert.NotNull(@class);
Assert.Equal("BaseType<dynamic>", @class.BaseType);
}
private RazorCodeDocument CreateDocument(string content)
{
using (var stream = new MemoryStream())
{
var bytes = Encoding.UTF8.GetBytes(content);
stream.Write(bytes, 0, bytes.Length);
stream.Seek(0L, SeekOrigin.Begin);
var source = RazorSourceDocument.ReadFrom(stream, "test.cshtml");
return RazorCodeDocument.Create(source);
}
}
private ClassDeclarationIRNode FindClassNode(RazorIRNode node)
{
var visitor = new ClassNodeVisitor();
visitor.Visit(node);
return visitor.Node;
}
private RazorEngine CreateEngine()
{
return RazorEngine.Create(b =>
{
// Notice we're not registering the ModelDirective.Pass here so we can run it on demand.
b.AddDirective(ModelDirective.Directive);
});
}
private DocumentIRNode CreateIRDocument(RazorEngine engine, RazorCodeDocument codeDocument)
{
for (var i = 0; i < engine.Phases.Count; i++)
{
var phase = engine.Phases[i];
phase.Execute(codeDocument);
if (phase is IRazorIRPhase)
{
break;
}
}
return codeDocument.GetIRDocument();
}
private string GetCSharpContent(RazorIRNode node)
{
var builder = new StringBuilder();
for (var i = 0; i < node.Children.Count; i++)
{
var child = node.Children[i] as CSharpTokenIRNode;
builder.Append(child.Content);
}
return builder.ToString();
}
private class ClassNodeVisitor : RazorIRNodeWalker
{
public ClassDeclarationIRNode Node { get; set; }
public override void VisitClass(ClassDeclarationIRNode node)
{
Node = node;
}
}
}
}

View File

@ -1,49 +0,0 @@
// 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.Text;
namespace Microsoft.AspNetCore.Mvc.Razor
{
public class StringTextBuffer : ITextBuffer, IDisposable
{
private string _buffer;
public bool Disposed { get; set; }
public StringTextBuffer(string buffer)
{
_buffer = buffer;
}
public int Length
{
get { return _buffer.Length; }
}
public int Position { get; set; }
public int Read()
{
if (Position >= _buffer.Length)
{
return -1;
}
return _buffer[Position++];
}
public int Peek()
{
if (Position >= _buffer.Length)
{
return -1;
}
return _buffer[Position];
}
public void Dispose()
{
Disposed = true;
}
}
}

View File

@ -1,58 +0,0 @@
[Microsoft.AspNetCore.Razor.TagHelpers.HtmlTargetElementAttribute("foo")]
public class __Generated__FooViewComponentTagHelper : Microsoft.AspNetCore.Razor.TagHelpers.TagHelper
{
private readonly global::Microsoft.AspNetCore.Mvc.IViewComponentHelper _viewComponentHelper = null;
public __Generated__FooViewComponentTagHelper(global::Microsoft.AspNetCore.Mvc.IViewComponentHelper viewComponentHelper)
{
_viewComponentHelper = viewComponentHelper;
}
[Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeNotBoundAttribute, global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewContextAttribute]
public global::Microsoft.AspNetCore.Mvc.Rendering.ViewContext ViewContext { get; set; }
public System.String Attribute { get; set; }
public override async global::System.Threading.Tasks.Task ProcessAsync(Microsoft.AspNetCore.Razor.TagHelpers.TagHelperContext context, Microsoft.AspNetCore.Razor.TagHelpers.TagHelperOutput output)
{
(_viewComponentHelper as global::Microsoft.AspNetCore.Mvc.ViewFeatures.IViewContextAware)?.Contextualize(ViewContext);
var viewContent = await _viewComponentHelper.InvokeAsync("Foo", new { Attribute });
output.TagName = null;
output.Content.SetHtmlContent(viewContent);
}
}
[Microsoft.AspNetCore.Razor.TagHelpers.HtmlTargetElementAttribute("bar")]
public class __Generated__BarViewComponentTagHelper : Microsoft.AspNetCore.Razor.TagHelpers.TagHelper
{
private readonly global::Microsoft.AspNetCore.Mvc.IViewComponentHelper _viewComponentHelper = null;
public __Generated__BarViewComponentTagHelper(global::Microsoft.AspNetCore.Mvc.IViewComponentHelper viewComponentHelper)
{
_viewComponentHelper = viewComponentHelper;
}
[Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeNotBoundAttribute, global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewContextAttribute]
public global::Microsoft.AspNetCore.Mvc.Rendering.ViewContext ViewContext { get; set; }
public System.String Attribute { get; set; }
public override async global::System.Threading.Tasks.Task ProcessAsync(Microsoft.AspNetCore.Razor.TagHelpers.TagHelperContext context, Microsoft.AspNetCore.Razor.TagHelpers.TagHelperOutput output)
{
(_viewComponentHelper as global::Microsoft.AspNetCore.Mvc.ViewFeatures.IViewContextAware)?.Contextualize(ViewContext);
var viewContent = await _viewComponentHelper.InvokeAsync("Bar", new { Attribute });
output.TagName = null;
output.Content.SetHtmlContent(viewContent);
}
}
[Microsoft.AspNetCore.Razor.TagHelpers.HtmlTargetElementAttribute("bee")]
public class __Generated__BeeViewComponentTagHelper : Microsoft.AspNetCore.Razor.TagHelpers.TagHelper
{
private readonly global::Microsoft.AspNetCore.Mvc.IViewComponentHelper _viewComponentHelper = null;
public __Generated__BeeViewComponentTagHelper(global::Microsoft.AspNetCore.Mvc.IViewComponentHelper viewComponentHelper)
{
_viewComponentHelper = viewComponentHelper;
}
[Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeNotBoundAttribute, global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewContextAttribute]
public global::Microsoft.AspNetCore.Mvc.Rendering.ViewContext ViewContext { get; set; }
public System.Collections.Generic.Dictionary<global::System.String, global::System.Collections.Generic.List<global::System.Int32>> Attribute { get; set; }
= new System.Collections.Generic.Dictionary<global::System.String, global::System.Collections.Generic.List<global::System.Int32>>();
public override async global::System.Threading.Tasks.Task ProcessAsync(Microsoft.AspNetCore.Razor.TagHelpers.TagHelperContext context, Microsoft.AspNetCore.Razor.TagHelpers.TagHelperOutput output)
{
(_viewComponentHelper as global::Microsoft.AspNetCore.Mvc.ViewFeatures.IViewContextAware)?.Contextualize(ViewContext);
var viewContent = await _viewComponentHelper.InvokeAsync("Bee", new { Attribute });
output.TagName = null;
output.Content.SetHtmlContent(viewContent);
}
}

View File

@ -0,0 +1,336 @@
// 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 System.IO;
using System.Linq;
using System.Text;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.Evolution;
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
using Xunit;
using ErrorSink = Microsoft.AspNetCore.Razor.Evolution.Legacy.ErrorSink;
namespace Microsoft.AspNetCore.Mvc.Razor.Host
{
public class ViewComponentTagHelperPassTest
{
[Fact]
public void ViewComponentTagHelperPass_Execute_IgnoresRegularTagHelper()
{
// Arrange
var codeDocument = CreateDocument(@"
@addTagHelper TestTagHelper, TestAssembly
<p foo=""17"">");
var tagHelpers = new[]
{
new TagHelperDescriptor()
{
AssemblyName = "TestAssembly",
TypeName = "TestTagHelper",
TagName = "p",
Attributes = new TagHelperAttributeDescriptor[]
{
new TagHelperAttributeDescriptor()
{
TypeName = "System.Int32",
Name = "Foo",
}
}
}
};
var engine = CreateEngine(tagHelpers);
var pass = new ViewComponentTagHelperPass()
{
Engine = engine,
};
var irDocument = CreateIRDocument(engine, codeDocument);
// Act
pass.Execute(codeDocument, irDocument);
// Assert
var @class = FindClassNode(irDocument);
Assert.Equal(2, @class.Children.Count); // No class node created for a VCTH
for (var i = 0; i < @class.Children.Count; i++)
{
Assert.IsNotType<CSharpStatementIRNode>(@class.Children[i]);
}
}
[Fact]
public void ViewComponentTagHelperPass_Execute_CreatesViewComponentTagHelper()
{
// Arrange
var codeDocument = CreateDocument(@"
@addTagHelper TestTagHelper, TestAssembly
<tagcloud foo=""17"">");
var tagHelpers = new[]
{
new TagHelperDescriptor()
{
AssemblyName = "TestAssembly",
TypeName = "TestTagHelper",
TagName = "tagcloud",
Attributes = new TagHelperAttributeDescriptor[]
{
new TagHelperAttributeDescriptor()
{
TypeName = "System.Int32",
Name = "Foo",
PropertyName = "Foo",
}
}
}
};
tagHelpers[0].PropertyBag.Add(ViewComponentTagHelperDescriptorConventions.ViewComponentNameKey, "TagCloud");
var engine = CreateEngine(tagHelpers);
var pass = new ViewComponentTagHelperPass()
{
Engine = engine,
};
var irDocument = CreateIRDocument(engine, codeDocument);
var expectedVCTHName = "AspNetCore.Generated_test.__Generated__TagCloudViewComponentTagHelper";
// Act
pass.Execute(codeDocument, irDocument);
// Assert
var tagHelper = FindTagHelperNode(irDocument);
Assert.Equal(expectedVCTHName, Assert.IsType<CreateTagHelperIRNode>(tagHelper.Children[1]).TagHelperTypeName);
Assert.Equal(expectedVCTHName, Assert.IsType<SetTagHelperPropertyIRNode>(tagHelper.Children[2]).TagHelperTypeName);
var @class = FindClassNode(irDocument);
Assert.Equal(3, @class.Children.Count);
var vcthClass = Assert.IsType<CSharpStatementIRNode>(@class.Children[2]);
Assert.Equal(@"[Microsoft.AspNetCore.Razor.TagHelpers.HtmlTargetElementAttribute(""tagcloud"")]
public class __Generated__TagCloudViewComponentTagHelper : Microsoft.AspNetCore.Razor.TagHelpers.TagHelper
{
private readonly global::Microsoft.AspNetCore.Mvc.IViewComponentHelper _helper = null;
public __Generated__TagCloudViewComponentTagHelper(global::Microsoft.AspNetCore.Mvc.IViewComponentHelper helper)
{
_helper = helper;
}
[Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeNotBoundAttribute, global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewContextAttribute]
public global::Microsoft.AspNetCore.Mvc.Rendering.ViewContext ViewContext { get; set; }
public System.Int32 Foo { get; set; }
public override async global::System.Threading.Tasks.Task ProcessAsync(Microsoft.AspNetCore.Razor.TagHelpers.TagHelperContext context, Microsoft.AspNetCore.Razor.TagHelpers.TagHelperOutput output)
{
(_helper as global::Microsoft.AspNetCore.Mvc.ViewFeatures.IViewContextAware)?.Contextualize(ViewContext);
var content = await _helper.InvokeAsync(""TagCloud"", new { Foo });
output.TagName = null;
output.Content.SetHtmlContent(content);
}
}
", vcthClass.Content);
}
[Fact]
public void ViewComponentTagHelperPass_Execute_CreatesViewComponentTagHelper_WithIndexer()
{
// Arrange
var codeDocument = CreateDocument(@"
@addTagHelper TestTagHelper, TestAssembly
<tagcloud tag-foo=""17"">");
var tagHelpers = new[]
{
new TagHelperDescriptor()
{
AssemblyName = "TestAssembly",
TypeName = "TestTagHelper",
TagName = "tagcloud",
Attributes = new TagHelperAttributeDescriptor[]
{
new TagHelperAttributeDescriptor()
{
TypeName = "System.Collections.Generic.Dictionary<System.String, System.Int32>",
Name = "foo",
PropertyName = "Tags",
IsIndexer = false,
},
new TagHelperAttributeDescriptor()
{
TypeName = "System.Collections.Generic.Dictionary<System.String, System.Int32>",
Name = "foo-",
PropertyName = "Tags",
IsIndexer = true,
}
}
}
};
tagHelpers[0].PropertyBag.Add(ViewComponentTagHelperDescriptorConventions.ViewComponentNameKey, "TagCloud");
var engine = CreateEngine(tagHelpers);
var pass = new ViewComponentTagHelperPass()
{
Engine = engine,
};
var irDocument = CreateIRDocument(engine, codeDocument);
var expectedVCTHName = "AspNetCore.Generated_test.__Generated__TagCloudViewComponentTagHelper";
// Act
pass.Execute(codeDocument, irDocument);
// Assert
var tagHelper = FindTagHelperNode(irDocument);
Assert.Equal(expectedVCTHName, Assert.IsType<CreateTagHelperIRNode>(tagHelper.Children[1]).TagHelperTypeName);
Assert.IsType<AddPreallocatedTagHelperHtmlAttributeIRNode>(tagHelper.Children[2]);
var @class = FindClassNode(irDocument);
Assert.Equal(4, @class.Children.Count);
var vcthClass = Assert.IsType<CSharpStatementIRNode>(@class.Children[3]);
Assert.Equal(@"[Microsoft.AspNetCore.Razor.TagHelpers.HtmlTargetElementAttribute(""tagcloud"")]
public class __Generated__TagCloudViewComponentTagHelper : Microsoft.AspNetCore.Razor.TagHelpers.TagHelper
{
private readonly global::Microsoft.AspNetCore.Mvc.IViewComponentHelper _helper = null;
public __Generated__TagCloudViewComponentTagHelper(global::Microsoft.AspNetCore.Mvc.IViewComponentHelper helper)
{
_helper = helper;
}
[Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeNotBoundAttribute, global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewContextAttribute]
public global::Microsoft.AspNetCore.Mvc.Rendering.ViewContext ViewContext { get; set; }
public System.Collections.Generic.Dictionary<System.String, System.Int32> Tags { get; set; }
= new System.Collections.Generic.Dictionary<System.String, System.Int32>();
public override async global::System.Threading.Tasks.Task ProcessAsync(Microsoft.AspNetCore.Razor.TagHelpers.TagHelperContext context, Microsoft.AspNetCore.Razor.TagHelpers.TagHelperOutput output)
{
(_helper as global::Microsoft.AspNetCore.Mvc.ViewFeatures.IViewContextAware)?.Contextualize(ViewContext);
var content = await _helper.InvokeAsync(""TagCloud"", new { Tags });
output.TagName = null;
output.Content.SetHtmlContent(content);
}
}
", vcthClass.Content);
}
private RazorCodeDocument CreateDocument(string content)
{
using (var stream = new MemoryStream())
{
var bytes = Encoding.UTF8.GetBytes(content);
stream.Write(bytes, 0, bytes.Length);
stream.Seek(0L, SeekOrigin.Begin);
var source = RazorSourceDocument.ReadFrom(stream, "test.cshtml");
return RazorCodeDocument.Create(source);
}
}
private RazorEngine CreateEngine(params TagHelperDescriptor[] tagHelpers)
{
return RazorEngine.Create(b =>
{
b.Features.Add(new MvcViewDocumentClassifierPass());
b.Features.Add(new TagHelperFeature(tagHelpers));
});
}
private DocumentIRNode CreateIRDocument(RazorEngine engine, RazorCodeDocument codeDocument)
{
for (var i = 0; i < engine.Phases.Count; i++)
{
var phase = engine.Phases[i];
phase.Execute(codeDocument);
if (phase is IRazorIRPhase)
{
break;
}
}
return codeDocument.GetIRDocument();
}
private ClassDeclarationIRNode FindClassNode(RazorIRNode node)
{
var visitor = new ClassDeclarationNodeVisitor();
visitor.Visit(node);
return visitor.Node;
}
private TagHelperIRNode FindTagHelperNode(RazorIRNode node)
{
var visitor = new TagHelperNodeVisitor();
visitor.Visit(node);
return visitor.Node;
}
private string GetCSharpContent(RazorIRNode node)
{
var builder = new StringBuilder();
for (var i = 0; i < node.Children.Count; i++)
{
var child = node.Children[i] as CSharpTokenIRNode;
builder.Append(child.Content);
}
return builder.ToString();
}
private class ClassDeclarationNodeVisitor : RazorIRNodeWalker
{
public ClassDeclarationIRNode Node { get; set; }
public override void VisitClass(ClassDeclarationIRNode node)
{
Node = node;
}
}
private class TagHelperNodeVisitor : RazorIRNodeWalker
{
public TagHelperIRNode Node { get; set; }
public override void VisitTagHelper(TagHelperIRNode node)
{
Node = node;
}
}
private class TagHelperFeature : ITagHelperFeature
{
public TagHelperFeature(TagHelperDescriptor[] tagHelpers)
{
Resolver = new TagHelperDescriptorResolver(tagHelpers);
}
public RazorEngine Engine { get; set; }
public ITagHelperDescriptorResolver Resolver { get; }
}
private class TagHelperDescriptorResolver : ITagHelperDescriptorResolver
{
public TagHelperDescriptorResolver(TagHelperDescriptor[] tagHelpers)
{
TagHelpers = tagHelpers;
}
public TagHelperDescriptor[] TagHelpers { get; }
public IEnumerable<TagHelperDescriptor> Resolve(ErrorSink errorSink)
{
return TagHelpers;
}
}
}
}