// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System.Web.Razor.Generator; using System.Web.Razor.Parser; using System.Web.Razor.Parser.SyntaxTree; using System.Web.Razor.Resources; using System.Web.Razor.Test.Framework; using System.Web.Razor.Text; using Xunit; namespace System.Web.Razor.Test.Parser.CSharp { public class CSharpHelperTest : CsHtmlMarkupParserTestBase { [Fact] public void ParseHelperCorrectlyParsesHelperWithNoSpaceInBody() { ParseDocumentTest("@helper Foo(){@Bar()}", new MarkupBlock( Factory.EmptyHtml(), new HelperBlock(new HelperCodeGenerator(new LocationTagged("Foo(){", 8, 0, 8), headerComplete: true), Factory.CodeTransition(), Factory.MetaCode("helper ").Accepts(AcceptedCharacters.None), Factory.Code("Foo(){").Hidden().Accepts(AcceptedCharacters.None), new StatementBlock( Factory.EmptyCSharp().AsStatement(), new ExpressionBlock( Factory.CodeTransition(), Factory.Code("Bar()") .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true) .Accepts(AcceptedCharacters.NonWhiteSpace)), Factory.EmptyCSharp().AsStatement()), Factory.Code("}").Hidden().Accepts(AcceptedCharacters.None)), Factory.EmptyHtml())); } [Fact] public void ParseHelperCorrectlyParsesIncompleteHelperPreceedingCodeBlock() { ParseDocumentTest(@"@helper @{}", new MarkupBlock( Factory.EmptyHtml(), new HelperBlock( Factory.CodeTransition(), Factory.MetaCode("helper")), Factory.Markup("\r\n"), new StatementBlock( Factory.CodeTransition(), Factory.MetaCode("{").Accepts(AcceptedCharacters.None), Factory.EmptyCSharp().AsStatement(), Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), Factory.EmptyHtml()), new RazorError( String.Format( RazorResources.ParseError_Unexpected_Character_At_Helper_Name_Start, RazorResources.ErrorComponent_Newline), 7, 0, 7)); } [Fact] public void ParseHelperRequiresSpaceBeforeSignature() { ParseDocumentTest("@helper{", new MarkupBlock( Factory.EmptyHtml(), new HelperBlock( Factory.CodeTransition(), Factory.MetaCode("helper")), Factory.Markup("{")), new RazorError( String.Format( RazorResources.ParseError_Unexpected_Character_At_Helper_Name_Start, String.Format(RazorResources.ErrorComponent_Character, "{")), 7, 0, 7)); } [Fact] public void ParseHelperOutputsErrorButContinuesIfLParenFoundAfterHelperKeyword() { ParseDocumentTest("@helper () {", new MarkupBlock( Factory.EmptyHtml(), new HelperBlock(new HelperCodeGenerator(new LocationTagged("() {", 8, 0, 8), headerComplete: true), Factory.CodeTransition(), Factory.MetaCode("helper ").Accepts(AcceptedCharacters.None), Factory.Code("() {").Hidden().Accepts(AcceptedCharacters.None), new StatementBlock( Factory.EmptyCSharp() .AsStatement() .AutoCompleteWith("}")))), new RazorError( String.Format( RazorResources.ParseError_Unexpected_Character_At_Helper_Name_Start, String.Format(RazorResources.ErrorComponent_Character, "(")), 8, 0, 8), new RazorError( String.Format( RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, "helper", "}", "{"), 1, 0, 1)); } [Fact] public void ParseHelperStatementOutputsMarkerHelperHeaderSpanOnceKeywordComplete() { ParseDocumentTest("@helper ", new MarkupBlock( Factory.EmptyHtml(), new HelperBlock(new HelperCodeGenerator(new LocationTagged(String.Empty, 8, 0, 8), headerComplete: false), Factory.CodeTransition(), Factory.MetaCode("helper ").Accepts(AcceptedCharacters.None), Factory.EmptyCSharp().Hidden())), new RazorError( String.Format( RazorResources.ParseError_Unexpected_Character_At_Helper_Name_Start, RazorResources.ErrorComponent_EndOfFile), 8, 0, 8)); } [Fact] public void ParseHelperStatementMarksHelperSpanAsCanGrowIfMissingTrailingSpace() { ParseDocumentTest("@helper", new MarkupBlock( Factory.EmptyHtml(), new HelperBlock( Factory.CodeTransition(), Factory.MetaCode("helper").Accepts(AcceptedCharacters.Any))), new RazorError( String.Format( RazorResources.ParseError_Unexpected_Character_At_Helper_Name_Start, RazorResources.ErrorComponent_EndOfFile), 7, 0, 7)); } [Fact] public void ParseHelperStatementCapturesWhitespaceToEndOfLineIfHelperStatementMissingName() { ParseDocumentTest(@"@helper ", new MarkupBlock( Factory.EmptyHtml(), new HelperBlock(new HelperCodeGenerator(new LocationTagged(" ", 8, 0, 8), headerComplete: false), Factory.CodeTransition(), Factory.MetaCode("helper ").Accepts(AcceptedCharacters.None), Factory.Code(" \r\n").Hidden()), Factory.Markup(@" ")), new RazorError( String.Format( RazorResources.ParseError_Unexpected_Character_At_Helper_Name_Start, RazorResources.ErrorComponent_Newline), 30, 0, 30)); } [Fact] public void ParseHelperStatementCapturesWhitespaceToEndOfLineIfHelperStatementMissingOpenParen() { ParseDocumentTest(@"@helper Foo ", new MarkupBlock( Factory.EmptyHtml(), new HelperBlock(new HelperCodeGenerator(new LocationTagged("Foo ", 8, 0, 8), headerComplete: false), Factory.CodeTransition(), Factory.MetaCode("helper ").Accepts(AcceptedCharacters.None), Factory.Code("Foo \r\n").Hidden()), Factory.Markup(" ")), new RazorError( String.Format(RazorResources.ParseError_MissingCharAfterHelperName, "("), 15, 0, 15)); } [Fact] public void ParseHelperStatementCapturesAllContentToEndOfFileIfHelperStatementMissingCloseParenInParameterList() { ParseDocumentTest(@"@helper Foo(Foo Bar Biz Boz", new MarkupBlock( Factory.EmptyHtml(), new HelperBlock(new HelperCodeGenerator(new LocationTagged("Foo(Foo Bar\r\nBiz\r\nBoz", 8, 0, 8), headerComplete: false), Factory.CodeTransition(), Factory.MetaCode("helper ").Accepts(AcceptedCharacters.None), Factory.Code("Foo(Foo Bar\r\nBiz\r\nBoz").Hidden())), new RazorError( RazorResources.ParseError_UnterminatedHelperParameterList, 11, 0, 11)); } [Fact] public void ParseHelperStatementCapturesWhitespaceToEndOfLineIfHelperStatementMissingOpenBraceAfterParameterList() { ParseDocumentTest(@"@helper Foo(string foo) ", new MarkupBlock( Factory.EmptyHtml(), new HelperBlock(new HelperCodeGenerator(new LocationTagged("Foo(string foo) ", 8, 0, 8), headerComplete: false), Factory.CodeTransition(), Factory.MetaCode("helper ").Accepts(AcceptedCharacters.None), Factory.Code("Foo(string foo) \r\n").Hidden())), new RazorError( String.Format(RazorResources.ParseError_MissingCharAfterHelperParameters, "{"), 29, 1, 0)); } [Fact] public void ParseHelperStatementContinuesParsingHelperUntilEOF() { ParseDocumentTest(@"@helper Foo(string foo) {

Foo

", new MarkupBlock( Factory.EmptyHtml(), new HelperBlock(new HelperCodeGenerator(new LocationTagged("Foo(string foo) {", 8, 0, 8), headerComplete: true), Factory.CodeTransition(), Factory.MetaCode("helper ").Accepts(AcceptedCharacters.None), Factory.Code(@"Foo(string foo) {").Hidden().Accepts(AcceptedCharacters.None), new StatementBlock( Factory.Code(" \r\n") .AsStatement() .AutoCompleteWith("}"), new MarkupBlock( Factory.Markup("

Foo

").Accepts(AcceptedCharacters.None)), Factory.EmptyCSharp().AsStatement()))), new RazorError( String.Format( RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, "helper", "}", "{"), 1, 0, 1)); } [Fact] public void ParseHelperStatementCorrectlyParsesHelperWithEmbeddedCode() { ParseDocumentTest(@"@helper Foo(string foo) {

@foo

}", new MarkupBlock( Factory.EmptyHtml(), new HelperBlock(new HelperCodeGenerator(new LocationTagged("Foo(string foo) {", 8, 0, 8), headerComplete: true), Factory.CodeTransition(), Factory.MetaCode("helper ").Accepts(AcceptedCharacters.None), Factory.Code(@"Foo(string foo) {").Hidden().Accepts(AcceptedCharacters.None), new StatementBlock( Factory.Code(" \r\n").AsStatement(), new MarkupBlock( Factory.Markup("

"), new ExpressionBlock( Factory.CodeTransition(), Factory.Code("foo") .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) .Accepts(AcceptedCharacters.NonWhiteSpace)), Factory.Markup("

\r\n").Accepts(AcceptedCharacters.None)), Factory.EmptyCSharp().AsStatement()), Factory.Code("}").Hidden().Accepts(AcceptedCharacters.None)), Factory.EmptyHtml())); } [Fact] public void ParseHelperStatementCorrectlyParsesHelperWithNewlinesBetweenCloseParenAndOpenBrace() { ParseDocumentTest(@"@helper Foo(string foo) {

@foo

}", new MarkupBlock( Factory.EmptyHtml(), new HelperBlock(new HelperCodeGenerator(new LocationTagged("Foo(string foo)\r\n\r\n\r\n\r\n{", 8, 0, 8), headerComplete: true), Factory.CodeTransition(), Factory.MetaCode("helper ").Accepts(AcceptedCharacters.None), Factory.Code("Foo(string foo)\r\n\r\n\r\n\r\n{").Hidden().Accepts(AcceptedCharacters.None), new StatementBlock( Factory.Code(" \r\n").AsStatement(), new MarkupBlock( Factory.Markup(@"

"), new ExpressionBlock( Factory.CodeTransition(), Factory.Code("foo") .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) .Accepts(AcceptedCharacters.NonWhiteSpace)), Factory.Markup("

\r\n").Accepts(AcceptedCharacters.None)), Factory.EmptyCSharp().AsStatement()), Factory.Code("}").Hidden().Accepts(AcceptedCharacters.None)), Factory.EmptyHtml())); } [Fact] public void ParseHelperStatementGivesWhitespaceAfterOpenBraceToMarkupInDesignMode() { ParseDocumentTest(@"@helper Foo(string foo) { ", new MarkupBlock( Factory.EmptyHtml(), new HelperBlock(new HelperCodeGenerator(new LocationTagged("Foo(string foo) {", 8, 0, 8), headerComplete: true), Factory.CodeTransition(), Factory.MetaCode("helper ").Accepts(AcceptedCharacters.None), Factory.Code(@"Foo(string foo) {").Hidden().Accepts(AcceptedCharacters.None), new StatementBlock( Factory.Code(" \r\n ") .AsStatement() .AutoCompleteWith("}")))), designTimeParser: true, expectedErrors: new[] { new RazorError( String.Format( RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, "helper", "}", "{"), new SourceLocation(1, 0, 1)) }); } [Fact] public void ParseHelperAcceptsNestedHelpersButOutputsError() { ParseDocumentTest(@"@helper Foo(string foo) { @helper Bar(string baz) { } }", new MarkupBlock( Factory.EmptyHtml(), new HelperBlock(new HelperCodeGenerator(new LocationTagged("Foo(string foo) {", 8, 0, 8), headerComplete: true), Factory.CodeTransition(), Factory.MetaCode("helper ").Accepts(AcceptedCharacters.None), Factory.Code(@"Foo(string foo) {").Hidden().Accepts(AcceptedCharacters.None), new StatementBlock( Factory.Code("\r\n ").AsStatement(), new HelperBlock(new HelperCodeGenerator(new LocationTagged("Bar(string baz) {", 39, 1, 12), headerComplete: true), Factory.CodeTransition(), Factory.MetaCode("helper ").Accepts(AcceptedCharacters.None), Factory.Code(@"Bar(string baz) {").Hidden().Accepts(AcceptedCharacters.None), new StatementBlock( Factory.Code("\r\n ").AsStatement()), Factory.Code("}").Hidden().Accepts(AcceptedCharacters.None)), Factory.Code("\r\n").AsStatement()), Factory.Code("}").Hidden().Accepts(AcceptedCharacters.None)), Factory.EmptyHtml()), designTimeParser: true, expectedErrors: new[] { new RazorError(RazorResources.ParseError_Helpers_Cannot_Be_Nested, 38, 1, 11) }); } } }