// 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 Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Razor; using Microsoft.AspNetCore.Razor.Chunks; using Microsoft.AspNetCore.Razor.Compilation.TagHelpers; using Microsoft.AspNetCore.Razor.Parser; using Microsoft.AspNetCore.Razor.Parser.Internal; using Microsoft.AspNetCore.Razor.Parser.SyntaxTree; using Moq; using Xunit; namespace Microsoft.AspNetCore.Mvc.Razor { public class MvcRazorCodeParserTest { public static TheoryData ViewImportsData { get { // chunkTrees, expectedDirectiveDescriptors return new TheoryData { { new[] { CreateChunkTree(new TagHelperPrefixDirectiveChunk { Prefix = "THP" }) }, new[] { CreateDirectiveDescriptor("THP", TagHelperDirectiveType.TagHelperPrefix) } }, { new[] { CreateChunkTree(new AddTagHelperChunk { LookupText = "ATH" }) }, new[] { CreateDirectiveDescriptor("ATH", TagHelperDirectiveType.AddTagHelper) } }, { new[] { CreateChunkTree( new AddTagHelperChunk { LookupText = "ATH1" }, new AddTagHelperChunk { LookupText = "ATH2" }) }, new[] { CreateDirectiveDescriptor("ATH1", TagHelperDirectiveType.AddTagHelper), CreateDirectiveDescriptor("ATH2", TagHelperDirectiveType.AddTagHelper) } }, { new[] { CreateChunkTree(new RemoveTagHelperChunk { LookupText = "RTH" }) }, new[] { CreateDirectiveDescriptor("RTH", TagHelperDirectiveType.RemoveTagHelper) } }, { new[] { CreateChunkTree( new RemoveTagHelperChunk { LookupText = "RTH1" }, new RemoveTagHelperChunk { LookupText = "RTH2" }) }, new[] { CreateDirectiveDescriptor("RTH1", TagHelperDirectiveType.RemoveTagHelper), CreateDirectiveDescriptor("RTH2", TagHelperDirectiveType.RemoveTagHelper) } }, { new[] { CreateChunkTree(new TagHelperPrefixDirectiveChunk { Prefix = "THP2" }), CreateChunkTree(new TagHelperPrefixDirectiveChunk { Prefix = "THP1" }), }, new[] { CreateDirectiveDescriptor("THP1", TagHelperDirectiveType.TagHelperPrefix) } }, { new[] { CreateChunkTree( new TagHelperPrefixDirectiveChunk { Prefix = "THP" }, new RemoveTagHelperChunk { LookupText = "RTH" }, new AddTagHelperChunk { LookupText = "ATH" }) }, new[] { CreateDirectiveDescriptor("RTH", TagHelperDirectiveType.RemoveTagHelper), CreateDirectiveDescriptor("ATH", TagHelperDirectiveType.AddTagHelper), CreateDirectiveDescriptor("THP", TagHelperDirectiveType.TagHelperPrefix), } }, { new[] { CreateChunkTree(new RemoveTagHelperChunk { LookupText = "RTH" }), CreateChunkTree( new LiteralChunk { Text = "Hello world" }, new AddTagHelperChunk { LookupText = "ATH" }), }, new[] { CreateDirectiveDescriptor("RTH", TagHelperDirectiveType.RemoveTagHelper), CreateDirectiveDescriptor("ATH", TagHelperDirectiveType.AddTagHelper), } }, { new[] { CreateChunkTree(new RemoveTagHelperChunk { LookupText = "RTH" }), CreateChunkTree( new LiteralChunk { Text = "Hello world" }, new AddTagHelperChunk { LookupText = "ATH" }), CreateChunkTree(new TagHelperPrefixDirectiveChunk { Prefix = "THP" }), }, new[] { CreateDirectiveDescriptor("RTH", TagHelperDirectiveType.RemoveTagHelper), CreateDirectiveDescriptor("ATH", TagHelperDirectiveType.AddTagHelper), CreateDirectiveDescriptor("THP", TagHelperDirectiveType.TagHelperPrefix), } }, { new[] { CreateChunkTree(new TagHelperPrefixDirectiveChunk { Prefix = "THP2" }), CreateChunkTree(new RemoveTagHelperChunk { LookupText = "RTH" }), CreateChunkTree(new AddTagHelperChunk { LookupText = "ATH" }), CreateChunkTree(new TagHelperPrefixDirectiveChunk { Prefix = "THP1" }), }, new[] { CreateDirectiveDescriptor("RTH", TagHelperDirectiveType.RemoveTagHelper), CreateDirectiveDescriptor("ATH", TagHelperDirectiveType.AddTagHelper), CreateDirectiveDescriptor("THP1", TagHelperDirectiveType.TagHelperPrefix), } }, }; } } [Theory] [MemberData(nameof(ViewImportsData))] public void GetTagHelperDescriptors_ReturnsExpectedDirectiveDescriptors( ChunkTree[] chunkTrees, TagHelperDirectiveDescriptor[] expectedDirectiveDescriptors) { // Arrange var builder = new BlockBuilder { Type = BlockType.Comment }; var block = new Block(builder); IList descriptors = null; var resolver = new Mock(); resolver.Setup(r => r.Resolve(It.IsAny())) .Callback((TagHelperDescriptorResolutionContext context) => { descriptors = context.DirectiveDescriptors; }) .Returns(Enumerable.Empty()) .Verifiable(); var baseParser = new RazorParser( new CSharpCodeParser(), new HtmlMarkupParser(), tagHelperDescriptorResolver: resolver.Object); var parser = new TestableMvcRazorParser(baseParser, chunkTrees, defaultInheritedChunks: new Chunk[0]); // Act parser.GetTagHelperDescriptorsPublic(block, errorSink: new ErrorSink()).ToArray(); // Assert Assert.NotNull(descriptors); Assert.Equal(expectedDirectiveDescriptors.Length, descriptors.Count); for (var i = 0; i < expectedDirectiveDescriptors.Length; i++) { var expected = expectedDirectiveDescriptors[i]; var actual = descriptors[i]; Assert.Equal(expected.DirectiveText, actual.DirectiveText, StringComparer.Ordinal); Assert.Equal(SourceLocation.Zero, actual.Location); Assert.Equal(expected.DirectiveType, actual.DirectiveType); } } [Theory] [InlineData("", "")] [InlineData(" ; ", "")] [InlineData(" ", "")] [InlineData(";;", "")] [InlineData("a", "a")] [InlineData("a;", "a")] [InlineData("abcd", "abcd")] [InlineData("abc;d", "abc;d")] [InlineData("a bc d", "a bc d")] [InlineData("a\t\tbc\td\t", "a\t\tbc\td")] [InlineData("abc;", "abc")] [InlineData(" abc;", "abc")] [InlineData("\tabc;", "abc")] [InlineData(";; abc;", ";; abc")] [InlineData(";;\tabc;", ";;\tabc")] [InlineData("\t;;abc;", ";;abc")] [InlineData("abc;; ;", "abc")] [InlineData("abc;;\t;", "abc")] [InlineData("\tabc \t;", "abc")] [InlineData("abc;;\r\n;", "abc")] [InlineData("abcd \n", "abcd")] [InlineData("\r\n\r \n\t abcd \t \t \n \r\n", "abcd")] [InlineData("pqrs\r", "pqrs")] public void RemoveWhitespaceAndTrailingSemicolons_ReturnsExpectedValues(string input, string expectedOutput) { // Arrange and Act var output = MvcRazorCodeParser.RemoveWhitespaceAndTrailingSemicolons(input); // Assert Assert.Equal(expectedOutput, output, StringComparer.Ordinal); } private static ChunkTree CreateChunkTree(params Chunk[] chunks) { return new ChunkTree { Children = chunks }; } private static TagHelperDirectiveDescriptor CreateDirectiveDescriptor( string directiveText, TagHelperDirectiveType directiveType) { return new TagHelperDirectiveDescriptor { DirectiveText = directiveText, Location = SourceLocation.Undefined, DirectiveType = directiveType }; } private class TestableMvcRazorParser : MvcRazorParser { public TestableMvcRazorParser( RazorParser parser, IReadOnlyList chunkTrees, IReadOnlyList defaultInheritedChunks) : base(parser, chunkTrees, defaultInheritedChunks, typeof(ModelExpression).FullName) { } public IEnumerable GetTagHelperDescriptorsPublic( Block documentRoot, ErrorSink errorSink) { return GetTagHelperDescriptors(documentRoot, errorSink); } } } }