Skip to content
This repository was archived by the owner on Dec 19, 2018. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a few tag helper samples that should cover most of the use-cases for tag helper implementations. I'll add more attribute related tag helper tests with #102 since that will be its focus.

// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Linq;
using System.Reflection;
using Microsoft.AspNet.Razor.Parser;
using Microsoft.AspNet.Razor.Parser.TagHelpers.Internal;
using Microsoft.AspNet.Razor.TagHelpers;
using Moq;
using Xunit;

namespace Microsoft.AspNet.Razor.Test.Generator
{
public class CSharpTagHelperRenderingTest : CSharpRazorCodeGeneratorTest
{
[Theory]
[InlineData("TagHelpersInSection")]
[InlineData("TagHelpersInHelper")]
public void TagHelpers_WithinHelpersAndSections_GeneratesExpectedOutput(string testType)
{
// Arrange
var propertyInfoMock = new Mock<PropertyInfo>();
propertyInfoMock.Setup(propertyInfo => propertyInfo.PropertyType).Returns(typeof(string));
propertyInfoMock.Setup(propertyInfo => propertyInfo.Name).Returns("BoundProperty");
var tagHelperDescriptorProvider = new TagHelperDescriptorProvider(
new TagHelperDescriptor[]
{
new TagHelperDescriptor("MyTagHelper",
"MyTagHelper",
ContentBehavior.None,
new [] {
new TagHelperAttributeDescriptor("BoundProperty",
propertyInfoMock.Object)
}),
new TagHelperDescriptor("NestedTagHelper", "NestedTagHelper", ContentBehavior.Modify)
});

// Act & Assert
RunTagHelperTest(testType, tagHelperDescriptorProvider);
}

[Theory]
[InlineData("SingleTagHelper")]
[InlineData("BasicTagHelpers")]
[InlineData("ComplexTagHelpers")]
public void TagHelpers_GenerateExpectedOutput(string testType)
{
// Arrange
var pFooPropertyInfo = new Mock<PropertyInfo>();
pFooPropertyInfo.Setup(propertyInfo => propertyInfo.PropertyType).Returns(typeof(int));
pFooPropertyInfo.Setup(propertyInfo => propertyInfo.Name).Returns("Foo");
var inputTypePropertyInfo = new Mock<PropertyInfo>();
inputTypePropertyInfo.Setup(propertyInfo => propertyInfo.PropertyType).Returns(typeof(string));
inputTypePropertyInfo.Setup(propertyInfo => propertyInfo.Name).Returns("Type");
var checkedPropertyInfo = new Mock<PropertyInfo>();
checkedPropertyInfo.Setup(propertyInfo => propertyInfo.PropertyType).Returns(typeof(bool));
checkedPropertyInfo.Setup(propertyInfo => propertyInfo.Name).Returns("Checked");
var tagHelperDescriptorProvider = new TagHelperDescriptorProvider(
new TagHelperDescriptor[]
{
new TagHelperDescriptor("p",
"PTagHelper",
ContentBehavior.None,
new [] {
new TagHelperAttributeDescriptor("foo", pFooPropertyInfo.Object)
}),
new TagHelperDescriptor("input",
"InputTagHelper",
ContentBehavior.None,
new TagHelperAttributeDescriptor[] {
new TagHelperAttributeDescriptor("type", inputTypePropertyInfo.Object)
}),
new TagHelperDescriptor("input",
"InputTagHelper2",
ContentBehavior.None,
new TagHelperAttributeDescriptor[] {
new TagHelperAttributeDescriptor("type", inputTypePropertyInfo.Object),
new TagHelperAttributeDescriptor("checked", checkedPropertyInfo.Object)
}),
});

// Act & Assert
RunTagHelperTest(testType, tagHelperDescriptorProvider);
}

[Fact]
public void TagHelpers_WithContentBehaviors_GenerateExpectedOutput()
{
// Arrange
var tagHelperDescriptorProvider = new TagHelperDescriptorProvider(
new TagHelperDescriptor[]
{
new TagHelperDescriptor("modify", "ModifyTagHelper", ContentBehavior.Modify),
new TagHelperDescriptor("none", "NoneTagHelper", ContentBehavior.None),
new TagHelperDescriptor("append", "AppendTagHelper", ContentBehavior.Append),
new TagHelperDescriptor("prepend", "PrependTagHelper", ContentBehavior.Prepend),
new TagHelperDescriptor("replace", "ReplaceTagHelper", ContentBehavior.Replace),
});

// Act & Assert
RunTagHelperTest("ContentBehaviorTagHelpers", tagHelperDescriptorProvider);
}

private void RunTagHelperTest(string testName, TagHelperDescriptorProvider tagHelperDescriptorProvider)
{
RunTest(
name: testName,
templateEngineConfig: (engine) =>
{
return new TagHelperTemplateEngine(engine, tagHelperDescriptorProvider);
});
}

private class TagHelperTemplateEngine : RazorTemplateEngine
{
private TagHelperDescriptorProvider _tagHelperDescriptorProvider;

public TagHelperTemplateEngine(RazorTemplateEngine engine, TagHelperDescriptorProvider tagHelperDescriptorProvider)
: base(engine.Host)
{
_tagHelperDescriptorProvider = tagHelperDescriptorProvider;
}

protected internal override RazorParser CreateParser()
{
var parser = base.CreateParser();
var tagHelperParseTreeRewriter = new TagHelperParseTreeRewriter(_tagHelperDescriptorProvider);

for (var i = 0; i < parser.Optimizers.Count; i++)
{
if (parser.Optimizers[i] is TagHelperParseTreeRewriter)
{
parser.Optimizers[i] = tagHelperParseTreeRewriter;
break;
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

throw something, Assert a failure, or silently parser.Optimizers.Add(parseTreeRewriter) if you haven't updated the list. probably need to do this either at the bottom of the for (e.g. else if (i == parser.Optimizers.Count - 1) ...) or add a bool variable to remember if you did something in the loop.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This all goes away in the PR from Add_Directive.

return parser;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ protected void RunTest(string name,
TestSpan[] spans = null,
TabTest tabTest = TabTest.Both,
Action<RazorEngineHost> hostConfig = null,
Func<RazorTemplateEngine, RazorTemplateEngine> templateEngineConfig = null,
Action<GeneratorResults> onResults = null)
{
bool testRun = false;
Expand All @@ -55,6 +56,7 @@ protected void RunTest(string name,
spans: spans,
withTabs: true,
hostConfig: hostConfig,
templateEngineConfig: templateEngineConfig,
onResults: onResults);
}

Expand All @@ -74,6 +76,7 @@ protected void RunTest(string name,
spans: spans,
withTabs: false,
hostConfig: hostConfig,
templateEngineConfig: templateEngineConfig,
onResults: onResults);
}

Expand All @@ -84,14 +87,15 @@ protected void RunTest(string name,
}

private void RunTestInternal(string name,
string baselineName,
bool generatePragmas,
bool designTimeMode,
IList<LineMapping> expectedDesignTimePragmas,
TestSpan[] spans,
bool withTabs,
Action<RazorEngineHost> hostConfig,
Action<GeneratorResults> onResults = null)
string baselineName,
bool generatePragmas,
bool designTimeMode,
IList<LineMapping> expectedDesignTimePragmas,
TestSpan[] spans,
bool withTabs,
Action<RazorEngineHost> hostConfig,
Func<RazorTemplateEngine, RazorTemplateEngine> templateEngineConfig,
Action<GeneratorResults> onResults = null)
{
// Load the test files
if (baselineName == null)
Expand All @@ -118,7 +122,8 @@ private void RunTestInternal(string name,
"Template",
"DefineSection",
"Instrumentation.BeginContext",
"Instrumentation.EndContext")
"Instrumentation.EndContext",
new GeneratedTagHelperContext())
{
LayoutPropertyName = "Layout",
ResolveUrlMethodName = "Href"
Expand All @@ -133,6 +138,11 @@ private void RunTestInternal(string name,

RazorTemplateEngine engine = new RazorTemplateEngine(host);

if(templateEngineConfig != null)
{
engine = templateEngineConfig(engine);
}

// Generate code for the file
GeneratorResults results = null;
using (var source = TestFile.Create(sourceLocation).OpenRead())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,14 @@ protected static RazorEngineHost CreateHost()
{
return new RazorEngineHost(new TLanguage())
{
GeneratedClassContext = new GeneratedClassContext("Execute", "Write", "WriteLiteral", "WriteTo", "WriteLiteralTo", "Template", "DefineSection"),
GeneratedClassContext = new GeneratedClassContext("Execute",
"Write",
"WriteLiteral",
"WriteTo",
"WriteLiteralTo",
"Template",
"DefineSection",
new GeneratedTagHelperContext()),
DesignTimeMode = true
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#pragma checksum "BasicTagHelpers.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "8d60e58d54168749dc71a0d6d3a95887b2adb5e7"
namespace TestOutput
{
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using System;
using System.Threading.Tasks;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Razor needs to add a using for the ITagHelperRunner / ITagHelperScopeManager namespace

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, this is a comment on #134


public class BasicTagHelpers
{
#line hidden
private System.IO.TextWriter __tagHelperStringValueBuffer = null;
private TagHelperExecutionContext __tagHelperExecutionContext = null;
private TagHelperRunner __tagHelperRunner = new TagHelperRunner();
private TagHelperScopeManager __tagHelperScopeManager = new TagHelperScopeManager();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment on #134: make __tagHelpersExecutionContext and __tagHelperScopeManager names singular (__tagHelperFoo)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm not sure I follow your comment on the scope manager. The variable name is singular

private PTagHelper __PTagHelper = null;
private InputTagHelper __InputTagHelper = null;
private InputTagHelper2 __InputTagHelper2 = null;
#line hidden
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: should this directive be generated above line 10? none of the field declarations were in the .cshtml file

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added an extra one above the fields to separate.

public BasicTagHelpers()
{
}

#pragma warning disable 1998
public override async Task ExecuteAsync()
{
Instrumentation.BeginContext(0, 47, true);
WriteLiteral("<div class=\"randomNonTagHelperAttribute\">\r\n ");
Instrumentation.EndContext();
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("p");
__PTagHelper = CreateTagHelper<PTagHelper>();
__tagHelperExecutionContext.Add(__PTagHelper);
__tagHelperExecutionContext.AddHtmlAttribute("class", "Hello World");
__tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result;
WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag());
Instrumentation.BeginContext(70, 10, true);
WriteLiteral("\r\n ");
Instrumentation.EndContext();
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("p");
__PTagHelper = CreateTagHelper<PTagHelper>();
__tagHelperExecutionContext.Add(__PTagHelper);
__tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result;
WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag());
WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag());
__tagHelperExecutionContext = __tagHelperScopeManager.End();
Instrumentation.BeginContext(87, 10, true);
WriteLiteral("\r\n ");
Instrumentation.EndContext();
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("input");
__InputTagHelper = CreateTagHelper<InputTagHelper>();
__tagHelperExecutionContext.Add(__InputTagHelper);
__InputTagHelper.Type = "text";
__tagHelperExecutionContext.AddTagHelperAttribute("type", __InputTagHelper.Type);
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
__tagHelperExecutionContext.Add(__InputTagHelper2);
__InputTagHelper2.Type = __InputTagHelper.Type;
__tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result;
WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag());
WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag());
__tagHelperExecutionContext = __tagHelperScopeManager.End();
Instrumentation.BeginContext(118, 10, true);
WriteLiteral("\r\n ");
Instrumentation.EndContext();
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("input");
__InputTagHelper = CreateTagHelper<InputTagHelper>();
__tagHelperExecutionContext.Add(__InputTagHelper);
__InputTagHelper.Type = "checkbox";
__tagHelperExecutionContext.AddTagHelperAttribute("type", __InputTagHelper.Type);
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
__tagHelperExecutionContext.Add(__InputTagHelper2);
__InputTagHelper2.Type = __InputTagHelper.Type;
__InputTagHelper2.Checked = true;
__tagHelperExecutionContext.AddTagHelperAttribute("checked", __InputTagHelper2.Checked);
__tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result;
WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag());
WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag());
__tagHelperExecutionContext = __tagHelperScopeManager.End();
Instrumentation.BeginContext(167, 6, true);
WriteLiteral("\r\n ");
Instrumentation.EndContext();
WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag());
__tagHelperExecutionContext = __tagHelperScopeManager.End();
Instrumentation.BeginContext(177, 8, true);
WriteLiteral("\r\n</div>");
Instrumentation.EndContext();
}
#pragma warning restore 1998
}
}
Loading