// 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.AspNet.Razor; using Microsoft.AspNet.Razor.Generator.Compiler; using Microsoft.AspNet.Razor.Parser; using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.TagHelpers; using Moq; using Xunit; namespace Microsoft.AspNet.Mvc.Razor { public class MvcRazorCodeParserTest { public static TheoryData GlobalImportData { get { // codeTrees, expectedDirectiveDescriptors return new TheoryData { { new[] { CreateCodeTree(new TagHelperPrefixDirectiveChunk { Prefix = "THP" }) }, new[] { CreateDirectiveDescriptor("THP", TagHelperDirectiveType.TagHelperPrefix) } }, { new[] { CreateCodeTree(new AddTagHelperChunk { LookupText = "ATH" }) }, new[] { CreateDirectiveDescriptor("ATH", TagHelperDirectiveType.AddTagHelper) } }, { new[] { CreateCodeTree( new AddTagHelperChunk { LookupText = "ATH1" }, new AddTagHelperChunk { LookupText = "ATH2" }) }, new[] { CreateDirectiveDescriptor("ATH1", TagHelperDirectiveType.AddTagHelper), CreateDirectiveDescriptor("ATH2", TagHelperDirectiveType.AddTagHelper) } }, { new[] { CreateCodeTree(new RemoveTagHelperChunk { LookupText = "RTH" }) }, new[] { CreateDirectiveDescriptor("RTH", TagHelperDirectiveType.RemoveTagHelper) } }, { new[] { CreateCodeTree( new RemoveTagHelperChunk { LookupText = "RTH1" }, new RemoveTagHelperChunk { LookupText = "RTH2" }) }, new[] { CreateDirectiveDescriptor("RTH1", TagHelperDirectiveType.RemoveTagHelper), CreateDirectiveDescriptor("RTH2", TagHelperDirectiveType.RemoveTagHelper) } }, { new[] { CreateCodeTree(new TagHelperPrefixDirectiveChunk { Prefix = "THP1" }), CreateCodeTree(new TagHelperPrefixDirectiveChunk { Prefix = "THP2" }), }, new[] { CreateDirectiveDescriptor("THP1", TagHelperDirectiveType.TagHelperPrefix) } }, { new[] { CreateCodeTree( 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[] { CreateCodeTree( new LiteralChunk { Text = "Hello world" }, new AddTagHelperChunk { LookupText = "ATH" }), CreateCodeTree(new RemoveTagHelperChunk { LookupText = "RTH" }) }, new[] { CreateDirectiveDescriptor("RTH", TagHelperDirectiveType.RemoveTagHelper), CreateDirectiveDescriptor("ATH", TagHelperDirectiveType.AddTagHelper), } }, { new[] { CreateCodeTree(new TagHelperPrefixDirectiveChunk { Prefix = "THP" }), CreateCodeTree( new LiteralChunk { Text = "Hello world" }, new AddTagHelperChunk { LookupText = "ATH" }), CreateCodeTree(new RemoveTagHelperChunk { LookupText = "RTH" }) }, new[] { CreateDirectiveDescriptor("RTH", TagHelperDirectiveType.RemoveTagHelper), CreateDirectiveDescriptor("ATH", TagHelperDirectiveType.AddTagHelper), CreateDirectiveDescriptor("THP", TagHelperDirectiveType.TagHelperPrefix), } }, { new[] { CreateCodeTree(new TagHelperPrefixDirectiveChunk { Prefix = "THP1" }), CreateCodeTree(new AddTagHelperChunk { LookupText = "ATH" }), CreateCodeTree(new RemoveTagHelperChunk { LookupText = "RTH" }), CreateCodeTree(new TagHelperPrefixDirectiveChunk { Prefix = "THP2" }), }, new[] { CreateDirectiveDescriptor("RTH", TagHelperDirectiveType.RemoveTagHelper), CreateDirectiveDescriptor("ATH", TagHelperDirectiveType.AddTagHelper), CreateDirectiveDescriptor("THP1", TagHelperDirectiveType.TagHelperPrefix), } }, }; } } [Theory] [MemberData(nameof(GlobalImportData))] public void GetTagHelperDescriptors_ReturnsExpectedDirectiveDescriptors( CodeTree[] codeTrees, 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, codeTrees, 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); } } public static TheoryData TrimSpacesAndCharsData { get { // input, trimCharacters, expectedOutput return new TheoryData { { "abcd", new char[] { }, "abcd" }, { " /.", new char[] { '/', '.' }, string.Empty }, { string.Empty, new char[] { }, string.Empty }, { " ", new char[] { }, string.Empty }, { " ", new char[] { }, string.Empty }, { " / ", new char[] { '/' }, string.Empty }, { " \t ", new char[] { '/' }, string.Empty }, { " ", new char[] { '/' }, string.Empty }, { " ", new char[] { '/' }, string.Empty }, { "/", new char[] { '/' }, string.Empty }, { "//", new char[] { '/' }, string.Empty }, { "// ", new char[] { '/' }, string.Empty }, { "/ ", new char[] { '/' }, string.Empty }, { " a ", new char[] { }, "a" }, { " a", new char[] { }, "a" }, { "a ", new char[] { }, "a" }, { " a ", new char[] { }, "a" }, { " a \n\r", new char[] { }, "a" }, { "\t\r a ", new char[] { }, "a" }, { "\ta ", new char[] { }, "a" }, { " a a ", new char[] { }, "a a" }, { " a ", new char[] { '/' }, "a" }, { " a", new char[] { '/' }, "a" }, { "a ", new char[] { '/' }, "a" }, { " a ", new char[] { '/' }, "a" }, { " a \n\r", new char[] { '/' }, "a" }, { "\t\r a ", new char[] { '/' }, "a" }, { "\ta ", new char[] { '/' }, "a" }, { " a a ", new char[] { '/' }, "a a" }, { " a ", new char[] { '/', ' ' }, "a" }, { " a", new char[] { '/', ' ' }, "a" }, { "a ", new char[] { '/', ' ' }, "a" }, { " a ", new char[] { '/', ' ' }, "a" }, { " a \n\r", new char[] { '/', ' ' }, "a" }, { "\t\r a ", new char[] { '/', ' ' }, "a" }, { "\ta ", new char[] { '/', ' ' }, "a" }, { " a a ", new char[] { '/', ' ' }, "a a" }, { "/ a ", new char[] { '/' }, "a" }, { " / a", new char[] { '/' }, "a" }, { "a / /", new char[] { '/' }, "a" }, { " a // //", new char[] { '/' }, "a" }, { " a \n\r//", new char[] { '/' }, "a" }, { "////\t\r a ", new char[] { '/' }, "a" }, { "\ta /", new char[] { '/' }, "a" }, { " a/ a ", new char[] { '/' }, "a/ a" }, { "/ a ", new char[] { '/', ' ' }, "a" }, { " / a", new char[] { '/', ' ' }, "a" }, { "a / /", new char[] { '/', ' ' }, "a" }, { " a // //", new char[] { '/', ' ' }, "a" }, { " a \n\r//", new char[] { '/', ' ' }, "a" }, { "////\t\r a ", new char[] { '/', ' ' }, "a" }, { "\ta /", new char[] { '/', ' ' }, "a" }, { " a/ a ", new char[] { '/', ' ' }, "a/ a" }, { " a /.", new char[] { '/', '.' }, "a" }, { " a", new char[] { '/', '.' }, "a" }, { "/. ./a ", new char[] { '/', '.' }, "a" }, { " a ", new char[] { '/', '.' }, "a" }, { " a \n\r", new char[] { '/', '.' }, "a" }, { "\t\r a ", new char[] { '/', '.' }, "a" }, { "\ta ", new char[] { '/', '.' }, "a" }, { "///..a/./a /. ./....", new char[] { '/', '.' }, "a/./a" }, }; } } [Theory] [MemberData(nameof(TrimSpacesAndCharsData))] public void TrimSpacesAndChars_GeneratesExpectedOutput( string input, char[] trimCharacters, string expectedOutput) { // Arrange & Act var output = MvcRazorCodeParser.TrimSpacesAndChars(input, trimCharacters); // Assert Assert.Equal(expectedOutput, output, StringComparer.Ordinal); } private static CodeTree CreateCodeTree(params Chunk[] chunks) { return new CodeTree { Chunks = chunks }; } private static TagHelperDirectiveDescriptor CreateDirectiveDescriptor( string directiveText, TagHelperDirectiveType directiveType) { return new TagHelperDirectiveDescriptor(directiveText, SourceLocation.Undefined, directiveType); } private class TestableMvcRazorParser : MvcRazorParser { public TestableMvcRazorParser(RazorParser parser, IReadOnlyList codeTrees, IReadOnlyList defaultInheritedChunks) : base(parser, codeTrees, defaultInheritedChunks) { } public IEnumerable GetTagHelperDescriptorsPublic( Block documentRoot, ErrorSink errorSink) { return GetTagHelperDescriptors(documentRoot, errorSink); } } } }