Add support for `@removeTagHelpers` inheritance from _ViewStart files
Fixes #1657
This commit is contained in:
parent
106b9fc30c
commit
6641997836
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.FileSystems;
|
||||
using Microsoft.AspNet.Razor;
|
||||
using Microsoft.AspNet.Razor.Generator.Compiler;
|
||||
|
|
@ -20,7 +21,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
|
|||
private readonly Dictionary<string, CodeTree> _parsedCodeTrees;
|
||||
private readonly MvcRazorHost _razorHost;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly IEnumerable<Chunk> _defaultInheritedChunks;
|
||||
private readonly IReadOnlyList<Chunk> _defaultInheritedChunks;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="ChunkInheritanceUtility"/>.
|
||||
|
|
@ -30,7 +31,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
|
|||
/// <param name="defaultInheritedChunks">Sequence of <see cref="Chunk"/>s inherited by default.</param>
|
||||
public ChunkInheritanceUtility([NotNull] MvcRazorHost razorHost,
|
||||
[NotNull] IFileSystem fileSystem,
|
||||
[NotNull] IEnumerable<Chunk> defaultInheritedChunks)
|
||||
[NotNull] IReadOnlyList<Chunk> defaultInheritedChunks)
|
||||
{
|
||||
_razorHost = razorHost;
|
||||
_fileSystem = fileSystem;
|
||||
|
|
@ -39,14 +40,16 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="IReadOnlyList{T}"/> of <see cref="Chunk"/> containing parsed results of _ViewStart files
|
||||
/// that are used for inheriting tag helpers and chunks to the page located at <paramref name="pagePath"/>.
|
||||
/// Gets an ordered <see cref="IReadOnlyList{T}"/> of parsed <see cref="CodeTree"/> for each _ViewStart that
|
||||
/// is applicable to the page located at <paramref name="pagePath"/>. The list is ordered so that the
|
||||
/// <see cref="CodeTree"/> for the _ViewStart closest to the <paramref name="pagePath"/> in the filesystem
|
||||
/// appears first.
|
||||
/// </summary>
|
||||
/// <param name="pagePath">The path of the page to locate inherited chunks for.</param>
|
||||
/// <returns>A <see cref="IReadOnlyList{T}"/> of <see cref="Chunk"/> from _ViewStart pages.</returns>
|
||||
public IReadOnlyList<Chunk> GetInheritedChunks([NotNull] string pagePath)
|
||||
/// <returns>A <see cref="IReadOnlyList{CodeTree}"/> of parsed _ViewStart <see cref="CodeTree"/>s.</returns>
|
||||
public IReadOnlyList<CodeTree> GetInheritedCodeTrees([NotNull] string pagePath)
|
||||
{
|
||||
var inheritedChunks = new List<Chunk>();
|
||||
var inheritedCodeTrees = new List<CodeTree>();
|
||||
|
||||
var templateEngine = new RazorTemplateEngine(_razorHost);
|
||||
foreach (var viewStartPath in ViewStartUtility.GetViewStartLocations(pagePath))
|
||||
|
|
@ -55,7 +58,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
|
|||
|
||||
if (_parsedCodeTrees.TryGetValue(viewStartPath, out codeTree))
|
||||
{
|
||||
inheritedChunks.AddRange(codeTree.Chunks);
|
||||
inheritedCodeTrees.Add(codeTree);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -68,25 +71,26 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
|
|||
// for the current _ViewStart to succeed.
|
||||
codeTree = ParseViewFile(templateEngine, fileInfo, viewStartPath);
|
||||
_parsedCodeTrees.Add(viewStartPath, codeTree);
|
||||
inheritedChunks.AddRange(codeTree.Chunks);
|
||||
|
||||
inheritedCodeTrees.Add(codeTree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inheritedChunks.AddRange(_defaultInheritedChunks);
|
||||
|
||||
return inheritedChunks;
|
||||
return inheritedCodeTrees;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges a list of chunks into the specified <paramref name="codeTree"/>.
|
||||
/// Merges <see cref="Chunk"/> inherited by default and <see cref="CodeTree"/> instances produced by parsing
|
||||
/// _ViewStart files into the specified <paramref name="codeTree"/>.
|
||||
/// </summary>
|
||||
/// <param name="codeTree">The <see cref="CodeTree"/> to merge.</param>
|
||||
/// <param name="inherited">The <see credit="IReadOnlyList{T}"/> of <see cref="Chunk"/> to merge.</param>
|
||||
/// <param name="codeTree">The <see cref="CodeTree"/> to merge in to.</param>
|
||||
/// <param name="inheritedCodeTrees"><see cref="IReadOnlyList{CodeTree}"/> inherited from _ViewStart
|
||||
/// files.</param>
|
||||
/// <param name="defaultModel">The list of chunks to merge.</param>
|
||||
public void MergeInheritedChunks([NotNull] CodeTree codeTree,
|
||||
[NotNull] IReadOnlyList<Chunk> inherited,
|
||||
string defaultModel)
|
||||
public void MergeInheritedCodeTrees([NotNull] CodeTree codeTree,
|
||||
[NotNull] IReadOnlyList<CodeTree> inheritedCodeTrees,
|
||||
string defaultModel)
|
||||
{
|
||||
var mergerMappings = GetMergerMappings(codeTree, defaultModel);
|
||||
IChunkMerger merger;
|
||||
|
|
@ -104,7 +108,12 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
|
|||
// In the second phase we invoke IChunkMerger.Merge for each chunk that has a mapped merger.
|
||||
// During this phase, the merger can either add to the CodeTree or ignore the chunk based on the merging
|
||||
// rules.
|
||||
foreach (var chunk in inherited)
|
||||
// Read the chunks outside in - that is chunks from the _ViewStart closest to the page get merged in first
|
||||
// and the furthest one last. This allows the merger to ignore a directive like @model that was previously
|
||||
// seen.
|
||||
var chunksToMerge = inheritedCodeTrees.SelectMany(tree => tree.Chunks)
|
||||
.Concat(_defaultInheritedChunks);
|
||||
foreach (var chunk in chunksToMerge)
|
||||
{
|
||||
if (mergerMappings.TryGetValue(chunk.GetType(), out merger))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -183,8 +183,8 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
/// <inheritdoc />
|
||||
public override RazorParser DecorateRazorParser([NotNull] RazorParser razorParser, string sourceFileName)
|
||||
{
|
||||
var inheritedChunks = ChunkInheritanceUtility.GetInheritedChunks(sourceFileName);
|
||||
return new MvcRazorParser(razorParser, inheritedChunks);
|
||||
var inheritedCodeTrees = ChunkInheritanceUtility.GetInheritedCodeTrees(sourceFileName);
|
||||
return new MvcRazorParser(razorParser, inheritedCodeTrees, DefaultInheritedChunks);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
@ -197,9 +197,9 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
public override CodeBuilder DecorateCodeBuilder([NotNull] CodeBuilder incomingBuilder,
|
||||
[NotNull] CodeBuilderContext context)
|
||||
{
|
||||
var inheritedChunks = ChunkInheritanceUtility.GetInheritedChunks(context.SourceFile);
|
||||
var inheritedChunks = ChunkInheritanceUtility.GetInheritedCodeTrees(context.SourceFile);
|
||||
|
||||
ChunkInheritanceUtility.MergeInheritedChunks(context.CodeTreeBuilder.CodeTree,
|
||||
ChunkInheritanceUtility.MergeInheritedCodeTrees(context.CodeTreeBuilder.CodeTree,
|
||||
inheritedChunks,
|
||||
DefaultModel);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// 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.Generator.Compiler;
|
||||
|
|
@ -8,6 +9,7 @@ using Microsoft.AspNet.Razor.Parser;
|
|||
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
||||
using Microsoft.AspNet.Razor.Parser.TagHelpers;
|
||||
using Microsoft.AspNet.Razor.TagHelpers;
|
||||
using Microsoft.AspNet.Razor.Text;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
|
|
@ -17,18 +19,23 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
/// </summary>
|
||||
public class MvcRazorParser : RazorParser
|
||||
{
|
||||
private readonly IReadOnlyList<Chunk> _viewStartChunks;
|
||||
private readonly IEnumerable<TagHelperDirectiveDescriptor> _viewStartDirectiveDescriptors;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="MvcRazorParser"/>.
|
||||
/// </summary>
|
||||
/// <param name="parser">The <see cref="RazorParser"/> to copy properties from.</param>
|
||||
/// <param name="viewStartChunks">The <see cref="IReadOnlyList{T}"/> of <see cref="Chunk"/>s that are inherited
|
||||
/// by parsed pages from _ViewStart files.</param>
|
||||
public MvcRazorParser(RazorParser parser, IReadOnlyList<Chunk> viewStartChunks)
|
||||
/// <param name="inheritedCodeTrees">The <see cref="IReadOnlyList{CodeTree}"/>s that are inherited
|
||||
/// from parsed pages from _ViewStart files.</param>
|
||||
/// <param name="defaultInheritedChunks">The <see cref="IReadOnlyList{Chunk}"/> inherited by
|
||||
/// default by all Razor pages in the application.</param>
|
||||
public MvcRazorParser([NotNull] RazorParser parser,
|
||||
[NotNull] IReadOnlyList<CodeTree> inheritedCodeTrees,
|
||||
[NotNull] IReadOnlyList<Chunk> defaultInheritedChunks)
|
||||
: base(parser)
|
||||
{
|
||||
_viewStartChunks = viewStartChunks;
|
||||
// Construct tag helper descriptors from @addTagHelper and @removeTagHelper chunks
|
||||
_viewStartDirectiveDescriptors = GetTagHelperDescriptors(inheritedCodeTrees, defaultInheritedChunks);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
@ -36,17 +43,47 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
[NotNull] Block documentRoot,
|
||||
[NotNull] ParserErrorSink errorSink)
|
||||
{
|
||||
// Grab all the @addtaghelper chunks from view starts and construct TagHelperDirectiveDescriptors
|
||||
var directiveDescriptors = _viewStartChunks.OfType<AddTagHelperChunk>()
|
||||
.Select(chunk => new TagHelperDirectiveDescriptor(
|
||||
chunk.LookupText,
|
||||
chunk.Start,
|
||||
TagHelperDirectiveType.AddTagHelper));
|
||||
|
||||
var visitor = new ViewStartAddRemoveTagHelperVisitor(TagHelperDescriptorResolver,
|
||||
directiveDescriptors,
|
||||
_viewStartDirectiveDescriptors,
|
||||
errorSink);
|
||||
var descriptors = visitor.GetDescriptors(documentRoot);
|
||||
return visitor.GetDescriptors(documentRoot);
|
||||
}
|
||||
|
||||
private static IEnumerable<TagHelperDirectiveDescriptor> GetTagHelperDescriptors(
|
||||
IReadOnlyList<CodeTree> inheritedCodeTrees,
|
||||
IReadOnlyList<Chunk> defaultInheritedChunks)
|
||||
{
|
||||
var descriptors = new List<TagHelperDirectiveDescriptor>();
|
||||
|
||||
// For tag helpers, the @removeTagHelper only applies tag helpers that were added prior to it.
|
||||
// Consequently we must visit tag helpers outside-in - furthest _ViewStart first and nearest one last. This
|
||||
// is different from the behavior of chunk merging where we visit the nearest one first and ignore chunks
|
||||
// that were previously visited.
|
||||
var chunksFromViewStarts = inheritedCodeTrees.Reverse()
|
||||
.SelectMany(tree => tree.Chunks);
|
||||
var chunksInOrder = defaultInheritedChunks.Concat(chunksFromViewStarts);
|
||||
foreach (var chunk in chunksInOrder)
|
||||
{
|
||||
var addHelperChunk = chunk as AddTagHelperChunk;
|
||||
if (addHelperChunk != null)
|
||||
{
|
||||
var descriptor = new TagHelperDirectiveDescriptor(addHelperChunk.LookupText,
|
||||
SourceLocation.Undefined,
|
||||
TagHelperDirectiveType.AddTagHelper);
|
||||
descriptors.Add(descriptor);
|
||||
}
|
||||
else
|
||||
{
|
||||
var removeHelperChunk = chunk as RemoveTagHelperChunk;
|
||||
if (removeHelperChunk != null)
|
||||
{
|
||||
var descriptor = new TagHelperDirectiveDescriptor(removeHelperChunk.LookupText,
|
||||
SourceLocation.Undefined,
|
||||
TagHelperDirectiveType.RemoveTagHelper);
|
||||
descriptors.Add(descriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,17 +54,40 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
Assert.Equal(expectedContent, responseContent);
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> TagHelpersAreInheritedFromViewStartPagesData
|
||||
{
|
||||
get
|
||||
{
|
||||
var expected1 =
|
||||
@"<root>root-content</root>
|
||||
|
||||
|
||||
<nested>nested-content</nested>";
|
||||
yield return new[] { "NestedViewStartTagHelper", expected1 };
|
||||
|
||||
var expected2 =
|
||||
@"layout:<root>root-content</root>
|
||||
|
||||
|
||||
<nested>nested-content</nested>";
|
||||
|
||||
yield return new[] { "ViewWithLayoutAndNestedTagHelper", expected2 };
|
||||
|
||||
var expected3 =
|
||||
@"layout:<root>root-content</root>
|
||||
|
||||
|
||||
page:<root/>
|
||||
<nested>nested-content</nested>";
|
||||
yield return new[] { "ViewWithInheritedRemoveTagHelper", expected3 };
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("NestedViewStartTagHelper")]
|
||||
[InlineData("ViewWithLayoutAndNestedTagHelper")]
|
||||
public async Task TagHelpersAreInheritedFromViewStartPages(string action)
|
||||
[MemberData(nameof(TagHelpersAreInheritedFromViewStartPagesData))]
|
||||
public async Task TagHelpersAreInheritedFromViewStartPages(string action, string expected)
|
||||
{
|
||||
// Arrange
|
||||
var expected = string.Join(Environment.NewLine,
|
||||
"<root>root-content</root>",
|
||||
"",
|
||||
"",
|
||||
"<nested>nested-content</nested>");
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
|
|
|
|||
|
|
@ -25,31 +25,41 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
|
|||
}
|
||||
|
||||
");
|
||||
var defaultChunks = new Chunk[]
|
||||
{
|
||||
new InjectChunk("MyTestHtmlHelper", "Html"),
|
||||
new UsingChunk { Namespace = "AppNamespace.Model" },
|
||||
};
|
||||
var host = new MvcRazorHost(fileSystem);
|
||||
var utility = new ChunkInheritanceUtility(host, fileSystem, new Chunk[0]);
|
||||
var utility = new ChunkInheritanceUtility(host, fileSystem, defaultChunks);
|
||||
|
||||
// Act
|
||||
var chunks = utility.GetInheritedChunks(@"Views\home\Index.cshtml");
|
||||
var codeTrees = utility.GetInheritedCodeTrees(@"Views\home\Index.cshtml");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(8, chunks.Count);
|
||||
Assert.IsType<LiteralChunk>(chunks[0]);
|
||||
Assert.Equal(2, codeTrees.Count);
|
||||
var viewStartChunks = codeTrees[0].Chunks;
|
||||
Assert.Equal(3, viewStartChunks.Count);
|
||||
|
||||
var usingChunk = Assert.IsType<UsingChunk>(chunks[1]);
|
||||
Assert.IsType<LiteralChunk>(viewStartChunks[0]);
|
||||
var usingChunk = Assert.IsType<UsingChunk>(viewStartChunks[1]);
|
||||
Assert.Equal("MyNamespace", usingChunk.Namespace);
|
||||
Assert.IsType<LiteralChunk>(viewStartChunks[2]);
|
||||
|
||||
Assert.IsType<LiteralChunk>(chunks[2]);
|
||||
Assert.IsType<LiteralChunk>(chunks[3]);
|
||||
viewStartChunks = codeTrees[1].Chunks;
|
||||
Assert.Equal(5, viewStartChunks.Count);
|
||||
|
||||
var injectChunk = Assert.IsType<InjectChunk>(chunks[4]);
|
||||
Assert.IsType<LiteralChunk>(viewStartChunks[0]);
|
||||
|
||||
var injectChunk = Assert.IsType<InjectChunk>(viewStartChunks[1]);
|
||||
Assert.Equal("MyHelper<TModel>", injectChunk.TypeName);
|
||||
Assert.Equal("Helper", injectChunk.MemberName);
|
||||
|
||||
var setBaseTypeChunk = Assert.IsType<SetBaseTypeChunk>(chunks[5]);
|
||||
var setBaseTypeChunk = Assert.IsType<SetBaseTypeChunk>(viewStartChunks[2]);
|
||||
Assert.Equal("MyBaseType", setBaseTypeChunk.TypeName);
|
||||
|
||||
Assert.IsType<StatementChunk>(chunks[6]);
|
||||
Assert.IsType<LiteralChunk>(chunks[7]);
|
||||
Assert.IsType<StatementChunk>(viewStartChunks[3]);
|
||||
Assert.IsType<LiteralChunk>(viewStartChunks[4]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -61,17 +71,22 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
|
|||
fileSystem.AddFile(@"Views\_Layout.cshtml", string.Empty);
|
||||
fileSystem.AddFile(@"Views\home\_not-viewstart.cshtml", string.Empty);
|
||||
var host = new MvcRazorHost(fileSystem);
|
||||
var utility = new ChunkInheritanceUtility(host, fileSystem, new Chunk[0]);
|
||||
var defaultChunks = new Chunk[]
|
||||
{
|
||||
new InjectChunk("MyTestHtmlHelper", "Html"),
|
||||
new UsingChunk { Namespace = "AppNamespace.Model" },
|
||||
};
|
||||
var utility = new ChunkInheritanceUtility(host, fileSystem, defaultChunks);
|
||||
|
||||
// Act
|
||||
var chunks = utility.GetInheritedChunks(@"Views\home\Index.cshtml");
|
||||
var codeTrees = utility.GetInheritedCodeTrees(@"Views\home\Index.cshtml");
|
||||
|
||||
// Assert
|
||||
Assert.Empty(chunks);
|
||||
Assert.Empty(codeTrees);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetInheritedChunks_ReturnsDefaultInheritedChunks()
|
||||
public void MergeInheritedChunks_MergesDefaultInheritedChunks()
|
||||
{
|
||||
// Arrange
|
||||
var fileSystem = new TestFileSystem();
|
||||
|
|
@ -83,19 +98,38 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
|
|||
new InjectChunk("MyTestHtmlHelper", "Html"),
|
||||
new UsingChunk { Namespace = "AppNamespace.Model" },
|
||||
};
|
||||
var inheritedCodeTrees = new CodeTree[]
|
||||
{
|
||||
new CodeTree
|
||||
{
|
||||
Chunks = new Chunk[]
|
||||
{
|
||||
new UsingChunk { Namespace = "InheritedNamespace" },
|
||||
new LiteralChunk { Text = "some text" }
|
||||
}
|
||||
},
|
||||
new CodeTree
|
||||
{
|
||||
Chunks = new Chunk[]
|
||||
{
|
||||
new UsingChunk { Namespace = "AppNamespace.Model" },
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var utility = new ChunkInheritanceUtility(host, fileSystem, defaultChunks);
|
||||
var codeTree = new CodeTree();
|
||||
|
||||
// Act
|
||||
var chunks = utility.GetInheritedChunks(@"Views\Home\Index.cshtml");
|
||||
utility.MergeInheritedCodeTrees(codeTree,
|
||||
inheritedCodeTrees,
|
||||
"dynamic");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(4, chunks.Count);
|
||||
var injectChunk = Assert.IsType<InjectChunk>(chunks[1]);
|
||||
Assert.Equal("DifferentHelper<TModel>", injectChunk.TypeName);
|
||||
Assert.Equal("Html", injectChunk.MemberName);
|
||||
|
||||
Assert.Same(defaultChunks[0], chunks[2]);
|
||||
Assert.Same(defaultChunks[1], chunks[3]);
|
||||
Assert.Equal(3, codeTree.Chunks.Count);
|
||||
Assert.Same(inheritedCodeTrees[0].Chunks[0], codeTree.Chunks[0]);
|
||||
Assert.Same(inheritedCodeTrees[1].Chunks[0], codeTree.Chunks[1]);
|
||||
Assert.Same(defaultChunks[0], codeTree.Chunks[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Razor.Generator.Compiler;
|
||||
using Microsoft.AspNet.Razor.Parser;
|
||||
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
||||
using Microsoft.AspNet.Razor.TagHelpers;
|
||||
using Microsoft.AspNet.Razor.Text;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
public class MvcRazorCodeParserTest
|
||||
{
|
||||
[Fact]
|
||||
public void GetTagHelperDescriptors_ReturnsDescriptorsFromViewStart()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new BlockBuilder { Type = BlockType.Comment };
|
||||
var block = new Block(builder);
|
||||
var codeTrees = new[]
|
||||
{
|
||||
new CodeTree
|
||||
{
|
||||
Chunks = new Chunk[]
|
||||
{
|
||||
new LiteralChunk { Text = "Hello world" },
|
||||
new AddTagHelperChunk { LookupText = "Add Tag Helper" },
|
||||
}
|
||||
},
|
||||
new CodeTree
|
||||
{
|
||||
Chunks = new[]
|
||||
{
|
||||
new RemoveTagHelperChunk { LookupText = "Remove Tag Helper" },
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
IList<TagHelperDirectiveDescriptor> descriptors = null;
|
||||
var resolver = new Mock<ITagHelperDescriptorResolver>();
|
||||
resolver.Setup(r => r.Resolve(It.IsAny<TagHelperDescriptorResolutionContext>()))
|
||||
.Callback((TagHelperDescriptorResolutionContext context) =>
|
||||
{
|
||||
descriptors = context.DirectiveDescriptors;
|
||||
})
|
||||
.Returns(Enumerable.Empty<TagHelperDescriptor>())
|
||||
.Verifiable();
|
||||
|
||||
var baseParser = new RazorParser(new CSharpCodeParser(),
|
||||
new HtmlMarkupParser(),
|
||||
resolver.Object);
|
||||
var parser = new TestableMvcRazorParser(baseParser, codeTrees, new Chunk[0]);
|
||||
var sink = new ParserErrorSink();
|
||||
|
||||
// Act
|
||||
var result = parser.GetTagHelperDescriptorsPublic(block, sink).ToArray();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(descriptors);
|
||||
Assert.Equal(2, descriptors.Count);
|
||||
|
||||
Assert.Equal("Remove Tag Helper", descriptors[0].LookupText);
|
||||
Assert.Equal(SourceLocation.Undefined, descriptors[0].Location);
|
||||
|
||||
Assert.Equal("Add Tag Helper", descriptors[1].LookupText);
|
||||
Assert.Equal(TagHelperDirectiveType.AddTagHelper, descriptors[1].DirectiveType);
|
||||
Assert.Equal(SourceLocation.Undefined, descriptors[1].Location);
|
||||
}
|
||||
|
||||
private class TestableMvcRazorParser : MvcRazorParser
|
||||
{
|
||||
public TestableMvcRazorParser(RazorParser parser,
|
||||
IReadOnlyList<CodeTree> codeTrees,
|
||||
IReadOnlyList<Chunk> defaultInheritedChunks)
|
||||
: base(parser, codeTrees, defaultInheritedChunks)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<TagHelperDescriptor> GetTagHelperDescriptorsPublic(
|
||||
Block documentRoot,
|
||||
ParserErrorSink errorSink)
|
||||
{
|
||||
return GetTagHelperDescriptors(documentRoot, errorSink);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -39,5 +39,10 @@ namespace TagHelpersWebSite.Controllers
|
|||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
public ViewResult ViewWithInheritedRemoveTagHelper()
|
||||
{
|
||||
return View("/Views/RemoveTagHelperViewStart/ViewWithInheritedRemoveTagHelper.cshtml");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
page:<root/>
|
||||
<nested>some-content</nested>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
@{
|
||||
Layout = "~/Views/Shared/_LayoutWithRootTagHelper.cshtml";
|
||||
}
|
||||
@removetaghelper "TagHelpersWebSite.TagHelpers.RootViewStartTagHelper, TagHelpersWebSite"
|
||||
@addtaghelper "TagHelpersWebSite.TagHelpers.NestedViewStartTagHelper, TagHelpersWebSite"
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
<root />
|
||||
layout:<root/>
|
||||
@RenderBody()
|
||||
Loading…
Reference in New Issue