Add tests to validate @removetaghelper functionality.
- Added utility methods to construct valid SyntaxTreeNodes that represent the @removetaghelper directive. - Added parse level unit tests to validate the @removetaghelper generates an accurate SyntaxTreeNode. - Added parse level unit tests to validate the @removetaghelper throws with bad formats. - Added TagHelperRegistration unit tests to validate the AddOrRemoveTagHelperCodeGenerators are understood and affect the descriptors found. - Added Designtime mapping tests to validate correct source mappings are made to ensure proper coloring and lack-of C# intellisense. - Added end-to-end tests to validate @removetaghelper can essentially disable TagHelpers on a page. #112
This commit is contained in:
parent
30221f7ce0
commit
7ab25918e0
|
|
@ -20,6 +20,7 @@ namespace Microsoft.AspNet.Razor.Parser
|
|||
internal static ISet<string> DefaultKeywords = new HashSet<string>()
|
||||
{
|
||||
SyntaxConstants.CSharp.AddTagHelperKeyword,
|
||||
SyntaxConstants.CSharp.RemoveTagHelperKeyword,
|
||||
"if",
|
||||
"do",
|
||||
"try",
|
||||
|
|
|
|||
|
|
@ -302,7 +302,14 @@ namespace Microsoft.AspNet.Razor.Test.Framework
|
|||
|
||||
public SpanConstructor AsAddTagHelper(string lookupText)
|
||||
{
|
||||
return _self.With(new AddTagHelperCodeGenerator(lookupText));
|
||||
return _self.With(
|
||||
new AddOrRemoveTagHelperCodeGenerator(removeTagHelperDescriptors: false, lookupText: lookupText));
|
||||
}
|
||||
|
||||
public SpanConstructor AsRemoveTagHelper(string lookupText)
|
||||
{
|
||||
return _self.With(
|
||||
new AddOrRemoveTagHelperCodeGenerator(removeTagHelperDescriptors: true, lookupText: lookupText));
|
||||
}
|
||||
|
||||
public SpanConstructor As(ISpanCodeGenerator codeGenerator)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,23 @@ namespace Microsoft.AspNet.Razor.Test.Generator
|
|||
{
|
||||
public class CSharpTagHelperRenderingTest : TagHelperTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void CSharpCodeGenerator_CorrectlyGeneratesMappings_ForRemoveTagHelperDirective()
|
||||
{
|
||||
// Act & Assert
|
||||
RunTagHelperTest("RemoveTagHelperDirective",
|
||||
designTimeMode: true,
|
||||
expectedDesignTimePragmas: new List<LineMapping>()
|
||||
{
|
||||
BuildLineMapping(documentAbsoluteIndex: 17,
|
||||
documentLineIndex: 0,
|
||||
generatedAbsoluteIndex: 442,
|
||||
generatedLineIndex: 14,
|
||||
characterOffsetIndex: 17,
|
||||
contentLength: 11)
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CSharpCodeGenerator_CorrectlyGeneratesMappings_ForAddTagHelperDirective()
|
||||
{
|
||||
|
|
@ -65,6 +82,7 @@ namespace Microsoft.AspNet.Razor.Test.Generator
|
|||
[Theory]
|
||||
[InlineData("SingleTagHelper")]
|
||||
[InlineData("BasicTagHelpers")]
|
||||
[InlineData("BasicTagHelpers.RemoveTagHelper")]
|
||||
[InlineData("ComplexTagHelpers")]
|
||||
public void TagHelpers_GenerateExpectedOutput(string testType)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -36,6 +36,30 @@ namespace Microsoft.AspNet.Razor
|
|||
Assert.Equal(addTagHelperChunk.LookupText, "some text");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddRemoveTagHelperChunk_AddsChunkToTopLevelCodeTree()
|
||||
{
|
||||
// Arrange
|
||||
var spanFactory = SpanFactory.CreateCsHtml();
|
||||
var builder = new CodeTreeBuilder();
|
||||
var block = new ExpressionBlock();
|
||||
var removeTagHelperDirective = spanFactory.MetaCode(SyntaxConstants.CSharp.RemoveTagHelperKeyword + " ");
|
||||
|
||||
// Act
|
||||
builder.StartChunkBlock<ExpressionBlockChunk>(block);
|
||||
builder.AddRemoveTagHelperChunk("some text", removeTagHelperDirective);
|
||||
builder.EndChunkBlock();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, builder.CodeTree.Chunks.Count);
|
||||
|
||||
var chunkBlock = Assert.IsType<ExpressionBlockChunk>(builder.CodeTree.Chunks.First());
|
||||
Assert.Empty(chunkBlock.Children);
|
||||
|
||||
var removeTagHelperChunk = Assert.IsType<RemoveTagHelperChunk>(builder.CodeTree.Chunks.Last());
|
||||
Assert.Equal(removeTagHelperChunk.LookupText, "some text");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddLiteralChunk_AddsChunkToCodeTree()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -12,6 +12,94 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
|
|||
{
|
||||
public class CSharpDirectivesTest : CsHtmlCodeParserTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void RemoveTagHelperDirective_Succeeds()
|
||||
{
|
||||
ParseBlockTest("@removetaghelper \"Foo\"",
|
||||
new DirectiveBlock(
|
||||
Factory.CodeTransition(),
|
||||
Factory.MetaCode(SyntaxConstants.CSharp.RemoveTagHelperKeyword + " ")
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
Factory.Code("\"Foo\"").AsRemoveTagHelper("Foo")));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveTagHelperDirective_SupportsSpaces()
|
||||
{
|
||||
ParseBlockTest("@removetaghelper \" Foo, Bar \" ",
|
||||
new DirectiveBlock(
|
||||
Factory.CodeTransition(),
|
||||
Factory.MetaCode(SyntaxConstants.CSharp.RemoveTagHelperKeyword + " ")
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
Factory.Code("\" Foo, Bar \" ").AsRemoveTagHelper(" Foo, Bar ")));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveTagHelperDirective_RequiresValue()
|
||||
{
|
||||
ParseBlockTest("@removetaghelper ",
|
||||
new DirectiveBlock(
|
||||
Factory.CodeTransition(),
|
||||
Factory.MetaCode(SyntaxConstants.CSharp.RemoveTagHelperKeyword + " ")
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
Factory.EmptyCSharp().AsRemoveTagHelper(string.Empty)),
|
||||
new RazorError(
|
||||
RazorResources.FormatParseError_DirectiveMustHaveValue(
|
||||
SyntaxConstants.CSharp.RemoveTagHelperKeyword),
|
||||
absoluteIndex: 17, lineIndex: 0, columnIndex: 17));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveTagHelperDirective_StartQuoteRequiresDoubleQuotesAroundValue()
|
||||
{
|
||||
ParseBlockTest("@removetaghelper \"Foo",
|
||||
new DirectiveBlock(
|
||||
Factory.CodeTransition(),
|
||||
Factory.MetaCode(SyntaxConstants.CSharp.RemoveTagHelperKeyword + " ")
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
Factory.Code("\"Foo").AsRemoveTagHelper("Foo")),
|
||||
new RazorError(
|
||||
RazorResources.ParseError_Unterminated_String_Literal,
|
||||
absoluteIndex: 17, lineIndex: 0, columnIndex: 17),
|
||||
new RazorError(
|
||||
RazorResources.FormatParseError_DirectiveMustBeSurroundedByQuotes(
|
||||
SyntaxConstants.CSharp.RemoveTagHelperKeyword),
|
||||
absoluteIndex: 17, lineIndex: 0, columnIndex: 17));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveTagHelperDirective_EndQuoteRequiresDoubleQuotesAroundValue()
|
||||
{
|
||||
ParseBlockTest("@removetaghelper Foo\"",
|
||||
new DirectiveBlock(
|
||||
Factory.CodeTransition(),
|
||||
Factory.MetaCode(SyntaxConstants.CSharp.RemoveTagHelperKeyword + " ")
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
Factory.Code("Foo\"").AsRemoveTagHelper("Foo")),
|
||||
new RazorError(
|
||||
RazorResources.ParseError_Unterminated_String_Literal,
|
||||
absoluteIndex: 20, lineIndex: 0, columnIndex: 20),
|
||||
new RazorError(
|
||||
RazorResources.FormatParseError_DirectiveMustBeSurroundedByQuotes(
|
||||
SyntaxConstants.CSharp.RemoveTagHelperKeyword),
|
||||
absoluteIndex: 17, lineIndex: 0, columnIndex: 17));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveTagHelperDirective_RequiresDoubleQuotesAroundValue()
|
||||
{
|
||||
ParseBlockTest("@removetaghelper Foo",
|
||||
new DirectiveBlock(
|
||||
Factory.CodeTransition(),
|
||||
Factory.MetaCode(SyntaxConstants.CSharp.RemoveTagHelperKeyword + " ")
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
Factory.Code("Foo").AsRemoveTagHelper("Foo")),
|
||||
new RazorError(
|
||||
RazorResources.FormatParseError_DirectiveMustBeSurroundedByQuotes(
|
||||
SyntaxConstants.CSharp.RemoveTagHelperKeyword),
|
||||
absoluteIndex: 17, lineIndex: 0, columnIndex: 17));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddTagHelperDirective_Succeeds()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,219 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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 Microsoft.AspNet.Razor.Parser;
|
||||
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
||||
using Microsoft.AspNet.Razor.Parser.TagHelpers;
|
||||
using Microsoft.AspNet.Razor.Test.Framework;
|
||||
#if !ASPNETCORE50
|
||||
using Moq;
|
||||
#endif
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.TagHelpers
|
||||
{
|
||||
public class AddOrRemoveTagHelperSpanVisitorTest
|
||||
{
|
||||
private static readonly SpanFactory Factory = SpanFactory.CreateCsHtml();
|
||||
|
||||
private static TagHelperDescriptor PTagHelperDescriptor
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TagHelperDescriptor("p", "PTagHelper", ContentBehavior.None);
|
||||
}
|
||||
}
|
||||
|
||||
private static TagHelperDescriptor DivTagHelperDescriptor
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TagHelperDescriptor("div", "DivTagHelper", ContentBehavior.None);
|
||||
}
|
||||
}
|
||||
|
||||
#if !ASPNETCORE50
|
||||
[Fact]
|
||||
public void GetDescriptors_InvokesResolveForEachLookup()
|
||||
{
|
||||
// Arrange
|
||||
var resolver = new Mock<ITagHelperDescriptorResolver>();
|
||||
resolver.Setup(mock => mock.Resolve(It.IsAny<string>())).Returns(Enumerable.Empty<TagHelperDescriptor>());
|
||||
var addOrRemoveTagHelperSpanVisitor = new AddOrRemoveTagHelperSpanVisitor(resolver.Object);
|
||||
var document = new MarkupBlock(
|
||||
Factory.Code("\"one\"").AsAddTagHelper("one"),
|
||||
Factory.Code("\"two\"").AsRemoveTagHelper("two"),
|
||||
Factory.Code("\"three\"").AsRemoveTagHelper("three"));
|
||||
|
||||
// Act
|
||||
var descriptors = addOrRemoveTagHelperSpanVisitor.GetDescriptors(document);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(descriptors);
|
||||
resolver.Verify(mock => mock.Resolve(It.IsAny<string>()), Times.Exactly(3));
|
||||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public void GetDescriptors_LocatesNestedRemoveTagHelperCodeGenerator()
|
||||
{
|
||||
// Arrange
|
||||
var resolver = new TestTagHelperDescriptorResolver(
|
||||
new Dictionary<string, IEnumerable<TagHelperDescriptor>>
|
||||
{
|
||||
{ "something", new[] { PTagHelperDescriptor } }
|
||||
});
|
||||
var addOrRemoveTagHelperSpanVisitor = new AddOrRemoveTagHelperSpanVisitor(resolver);
|
||||
var document = new MarkupBlock(
|
||||
new DirectiveBlock(
|
||||
Factory.CodeTransition(),
|
||||
Factory.MetaCode(SyntaxConstants.CSharp.RemoveTagHelperKeyword + " ")
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
Factory.Code("\"something\"").AsRemoveTagHelper("something"))
|
||||
);
|
||||
|
||||
// Act
|
||||
var descriptors = addOrRemoveTagHelperSpanVisitor.GetDescriptors(document);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(descriptors);
|
||||
var lookup = Assert.Single(resolver.Lookups);
|
||||
Assert.Equal("something", lookup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDescriptors_RemovesSpecifiedTagHelper()
|
||||
{
|
||||
// Arrange
|
||||
var resolver = new TestTagHelperDescriptorResolver(
|
||||
new Dictionary<string, IEnumerable<TagHelperDescriptor>>
|
||||
{
|
||||
{ "twoTagHelpers", new[] { PTagHelperDescriptor, DivTagHelperDescriptor } },
|
||||
{ "singleTagHelper", new [] { PTagHelperDescriptor } }
|
||||
});
|
||||
var addOrRemoveTagHelperSpanVisitor = new AddOrRemoveTagHelperSpanVisitor(resolver);
|
||||
var document = new MarkupBlock(
|
||||
Factory.Code("\"twoTagHelpers\"").AsAddTagHelper("twoTagHelpers"),
|
||||
Factory.Code("\"singleTagHelper\"").AsRemoveTagHelper("singleTagHelper"));
|
||||
|
||||
// Act
|
||||
var descriptors = addOrRemoveTagHelperSpanVisitor.GetDescriptors(document);
|
||||
|
||||
// Assert
|
||||
var descriptor = Assert.Single(descriptors);
|
||||
Assert.Equal(DivTagHelperDescriptor, descriptor, TagHelperDescriptorComparer.Default);
|
||||
Assert.Equal(2, resolver.Lookups.Count);
|
||||
Assert.Contains("twoTagHelpers", resolver.Lookups);
|
||||
Assert.Contains("singleTagHelper", resolver.Lookups);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDescriptors_RemovesAddedTagHelpers()
|
||||
{
|
||||
// Arrange
|
||||
var resolver = new TestTagHelperDescriptorResolver(
|
||||
new Dictionary<string, IEnumerable<TagHelperDescriptor>>
|
||||
{
|
||||
{ "twoTagHelpers", new[] { PTagHelperDescriptor, DivTagHelperDescriptor } }
|
||||
});
|
||||
var addOrRemoveTagHelperSpanVisitor = new AddOrRemoveTagHelperSpanVisitor(resolver);
|
||||
var document = new MarkupBlock(
|
||||
Factory.Code("\"twoTagHelpers\"").AsAddTagHelper("twoTagHelpers"),
|
||||
Factory.Code("\"twoTagHelpers\"").AsRemoveTagHelper("twoTagHelpers"));
|
||||
|
||||
// Act
|
||||
var descriptors = addOrRemoveTagHelperSpanVisitor.GetDescriptors(document);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(descriptors);
|
||||
Assert.Equal(Enumerable.Repeat("twoTagHelpers", 2), resolver.Lookups);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDescriptors_RemoveTagHelper_OrderMatters()
|
||||
{
|
||||
// Arrange
|
||||
var expectedDescriptors = new[] { PTagHelperDescriptor, DivTagHelperDescriptor };
|
||||
var resolver = new TestTagHelperDescriptorResolver(
|
||||
new Dictionary<string, IEnumerable<TagHelperDescriptor>>
|
||||
{
|
||||
{ "twoTagHelpers", expectedDescriptors }
|
||||
});
|
||||
var addOrRemoveTagHelperSpanVisitor = new AddOrRemoveTagHelperSpanVisitor(resolver);
|
||||
var document = new MarkupBlock(
|
||||
Factory.Code("\"twoTagHelpers\"").AsRemoveTagHelper("twoTagHelpers"),
|
||||
Factory.Code("\"twoTagHelpers\"").AsAddTagHelper("twoTagHelpers"));
|
||||
|
||||
// Act
|
||||
var descriptors = addOrRemoveTagHelperSpanVisitor.GetDescriptors(document);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedDescriptors, descriptors, TagHelperDescriptorComparer.Default);
|
||||
Assert.Equal(Enumerable.Repeat("twoTagHelpers", 2), resolver.Lookups);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDescriptors_RemoveTagHelperInDocument_ThrowsIfNullResolver()
|
||||
{
|
||||
// Arrange
|
||||
var addOrRemoveTagHelperSpanVisitor = new AddOrRemoveTagHelperSpanVisitor(descriptorResolver: null);
|
||||
var document = new MarkupBlock(
|
||||
Factory.Code("\"something\"").AsRemoveTagHelper("something"));
|
||||
var expectedMessage = "Cannot use directive 'removetaghelper' when a Microsoft.AspNet.Razor.TagHelpers." +
|
||||
"ITagHelperDescriptorResolver has not been provided to the Microsoft.AspNet.Razor." +
|
||||
"Parser.RazorParser.";
|
||||
|
||||
// Act & Assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
addOrRemoveTagHelperSpanVisitor.GetDescriptors(document);
|
||||
});
|
||||
|
||||
Assert.Equal(expectedMessage, ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDescriptors_RemoveTagHelperNotInDocument_DoesNotThrow()
|
||||
{
|
||||
// Arrange
|
||||
var addOrRemoveTagHelperSpanVisitor = new AddOrRemoveTagHelperSpanVisitor(descriptorResolver: null);
|
||||
var document = new MarkupBlock(Factory.Markup("Hello World"));
|
||||
|
||||
// Act & Assert
|
||||
Assert.DoesNotThrow(() => addOrRemoveTagHelperSpanVisitor.GetDescriptors(document));
|
||||
}
|
||||
|
||||
// TODO: Add @addtaghelper directive unit tests. Tracked by https://github.com/aspnet/Razor/issues/202.
|
||||
|
||||
private class TestTagHelperDescriptorResolver : ITagHelperDescriptorResolver
|
||||
{
|
||||
private readonly IReadOnlyDictionary<string, IEnumerable<TagHelperDescriptor>> _lookupTable;
|
||||
|
||||
public TestTagHelperDescriptorResolver(
|
||||
IReadOnlyDictionary<string, IEnumerable<TagHelperDescriptor>> lookupTable)
|
||||
{
|
||||
_lookupTable = lookupTable;
|
||||
|
||||
Lookups = new List<string>();
|
||||
}
|
||||
|
||||
public List<string> Lookups { get; private set; }
|
||||
|
||||
public IEnumerable<TagHelperDescriptor> Resolve(string lookupText)
|
||||
{
|
||||
Lookups.Add(lookupText);
|
||||
|
||||
IEnumerable<TagHelperDescriptor> descriptors;
|
||||
if (_lookupTable.TryGetValue(lookupText, out descriptors))
|
||||
{
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
return Enumerable.Empty<TagHelperDescriptor>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
#pragma checksum "BasicTagHelpers.RemoveTagHelper.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "631e4af360bd37f540b637596d5f059c7abc0ac2"
|
||||
namespace TestOutput
|
||||
{
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class BasicTagHelpers.RemoveTagHelper
|
||||
{
|
||||
#line hidden
|
||||
public BasicTagHelpers.RemoveTagHelper()
|
||||
{
|
||||
}
|
||||
|
||||
#pragma warning disable 1998
|
||||
public override async Task ExecuteAsync()
|
||||
{
|
||||
Instrumentation.BeginContext(60, 187, true);
|
||||
WriteLiteral("\r\n<div class=\"randomNonTagHelperAttribute\">\r\n <p class=\"Hello World\">\r\n " +
|
||||
" <p></p>\r\n <input type=\"text\" />\r\n <input type=\"checkbox\" checked=" +
|
||||
"\"true\"/>\r\n </p>\r\n</div>");
|
||||
Instrumentation.EndContext();
|
||||
}
|
||||
#pragma warning restore 1998
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
namespace TestOutput
|
||||
{
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class RemoveTagHelperDirective
|
||||
{
|
||||
private static object @__o;
|
||||
private void @__RazorDesignTimeHelpers__()
|
||||
{
|
||||
#pragma warning disable 219
|
||||
string __tagHelperDirectiveSyntaxHelper = null;
|
||||
__tagHelperDirectiveSyntaxHelper =
|
||||
#line 1 "RemoveTagHelperDirective.cshtml"
|
||||
"something"
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
;
|
||||
#pragma warning restore 219
|
||||
}
|
||||
#line hidden
|
||||
public RemoveTagHelperDirective()
|
||||
{
|
||||
}
|
||||
|
||||
#pragma warning disable 1998
|
||||
public override async Task ExecuteAsync()
|
||||
{
|
||||
}
|
||||
#pragma warning restore 1998
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
@addtaghelper "something"
|
||||
@removetaghelper "doesntmatter"
|
||||
|
||||
<div class="randomNonTagHelperAttribute">
|
||||
<p class="Hello World">
|
||||
<p></p>
|
||||
<input type="text" />
|
||||
<input type="checkbox" checked="true"/>
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -0,0 +1 @@
|
|||
@removetaghelper "something"
|
||||
Loading…
Reference in New Issue