aspnetcore/test/Microsoft.AspNetCore.Razor..../Legacy/CSharpBlockTest.cs

687 lines
22 KiB
C#

// 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 Xunit;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
public class CSharpBlockTest : CsHtmlCodeParserTestBase
{
public CSharpBlockTest()
{
UseBaselineTests = true;
}
[Fact]
public void ParseBlock_NestedCodeBlockWithCSharpAt()
{
ParseBlockTest("{ if (true) { var val = @x; if (val != 3) { } } }");
}
[Fact]
public void ParseBlock_NestedCodeBlockWithMarkupSetsDotAsMarkup()
{
ParseBlockTest("if (true) { @if(false) { <div>@something.</div> } }");
}
[Fact]
public void BalancingBracketsIgnoresStringLiteralCharactersAndBracketsInsideSingleLineComments()
{
SingleSpanBlockTest(@"if(foo) {
// bar } "" baz '
zoop();
}");
}
[Fact]
public void NestedCodeBlockWithAtDoesntCauseError()
{
ParseBlockTest("if (true) { @if(false) { } }");
}
[Fact]
public void BalancingBracketsIgnoresStringLiteralCharactersAndBracketsInsideBlockComments()
{
SingleSpanBlockTest(
@"if(foo) {
/* bar } "" */ ' baz } '
zoop();
}");
}
[Fact]
public void ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsForKeyword()
{
SingleSpanBlockTest(
"for(int i = 0; i < 10; new Foo { Bar = \"baz\" }) { Debug.WriteLine(@\"foo } bar\"); }");
}
[Fact]
public void ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsForeachKeyword()
{
SingleSpanBlockTest(
"foreach(int i = 0; i < 10; new Foo { Bar = \"baz\" }) { Debug.WriteLine(@\"foo } bar\"); }");
}
[Fact]
public void ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsWhileKeyword()
{
SingleSpanBlockTest(
"while(int i = 0; i < 10; new Foo { Bar = \"baz\" }) { Debug.WriteLine(@\"foo } bar\"); }");
}
[Fact]
public void ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsUsingKeywordFollowedByParen()
{
SingleSpanBlockTest(
"using(int i = 0; i < 10; new Foo { Bar = \"baz\" }) { Debug.WriteLine(@\"foo } bar\"); }");
}
[Fact]
public void ParseBlockSupportsUsingsNestedWithinOtherBlocks()
{
SingleSpanBlockTest(
"if(foo) { using(int i = 0; i < 10; new Foo { Bar = \"baz\" }) { Debug.WriteLine(@\"foo } bar\"); } }");
}
[Fact]
public void ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsIfKeywordWithNoElseBranches()
{
SingleSpanBlockTest(
"if(int i = 0; i < 10; new Foo { Bar = \"baz\" }) { Debug.WriteLine(@\"foo } bar\"); }");
}
[Fact]
public void ParseBlockAllowsEmptyBlockStatement()
{
SingleSpanBlockTest("if(false) { }");
}
[Fact]
public void ParseBlockTerminatesParenBalancingAtEOF()
{
ImplicitExpressionTest(
"Html.En(code()", "Html.En(code()");
}
[Fact]
public void ParseBlockSupportsBlockCommentBetweenIfAndElseClause()
{
SingleSpanBlockTest(
"if(foo) { bar(); } /* Foo */ /* Bar */ else { baz(); }");
}
[Fact]
public void ParseBlockSupportsRazorCommentBetweenIfAndElseClause()
{
RunRazorCommentBetweenClausesTest(
"if(foo) { bar(); } ", " else { baz(); }");
}
[Fact]
public void ParseBlockSupportsBlockCommentBetweenElseIfAndElseClause()
{
SingleSpanBlockTest(
"if(foo) { bar(); } else if(bar) { baz(); } /* Foo */ /* Bar */ else { biz(); }");
}
[Fact]
public void ParseBlockSupportsRazorCommentBetweenElseIfAndElseClause()
{
RunRazorCommentBetweenClausesTest(
"if(foo) { bar(); } else if(bar) { baz(); } ", " else { baz(); }");
}
[Fact]
public void ParseBlockSupportsBlockCommentBetweenIfAndElseIfClause()
{
SingleSpanBlockTest(
"if(foo) { bar(); } /* Foo */ /* Bar */ else if(bar) { baz(); }");
}
[Fact]
public void ParseBlockSupportsRazorCommentBetweenIfAndElseIfClause()
{
RunRazorCommentBetweenClausesTest("if(foo) { bar(); } ", " else if(bar) { baz(); }");
}
[Fact]
public void ParseBlockSupportsLineCommentBetweenIfAndElseClause()
{
SingleSpanBlockTest(@"if(foo) { bar(); }
// Foo
// Bar
else { baz(); }");
}
[Fact]
public void ParseBlockSupportsLineCommentBetweenElseIfAndElseClause()
{
SingleSpanBlockTest(@"if(foo) { bar(); } else if(bar) { baz(); }
// Foo
// Bar
else { biz(); }");
}
[Fact]
public void ParseBlockSupportsLineCommentBetweenIfAndElseIfClause()
{
SingleSpanBlockTest(@"if(foo) { bar(); }
// Foo
// Bar
else if(bar) { baz(); }");
}
[Fact]
public void ParseBlockParsesElseIfBranchesOfIfStatement()
{
const string ifStatement = @"if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
Debug.WriteLine(@""foo } bar"");
}";
const string elseIfBranch = @" else if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
Debug.WriteLine(@""bar } baz"");
}";
const string document = ifStatement + elseIfBranch;
SingleSpanBlockTest(document);
}
[Fact]
public void ParseBlockParsesMultipleElseIfBranchesOfIfStatement()
{
const string ifStatement = @"if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
Debug.WriteLine(@""foo } bar"");
}";
const string elseIfBranch = @" else if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
Debug.WriteLine(@""bar } baz"");
}";
const string document = ifStatement + elseIfBranch + elseIfBranch + elseIfBranch + elseIfBranch;
SingleSpanBlockTest(document);
}
[Fact]
public void ParseBlockParsesMultipleElseIfBranchesOfIfStatementFollowedByOneElseBranch()
{
const string ifStatement = @"if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
Debug.WriteLine(@""foo } bar"");
}";
const string elseIfBranch = @" else if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
Debug.WriteLine(@""bar } baz"");
}";
const string elseBranch = @" else { Debug.WriteLine(@""bar } baz""); }";
const string document = ifStatement + elseIfBranch + elseIfBranch + elseBranch;
SingleSpanBlockTest(document);
}
[Fact]
public void ParseBlockStopsParsingCodeAfterElseBranch()
{
const string ifStatement = @"if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
Debug.WriteLine(@""foo } bar"");
}";
const string elseIfBranch = @" else if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
Debug.WriteLine(@""bar } baz"");
}";
const string elseBranch = @" else { Debug.WriteLine(@""bar } baz""); }";
const string document = ifStatement + elseIfBranch + elseBranch + elseIfBranch;
ParseBlockTest(document);
}
[Fact]
public void ParseBlockStopsParsingIfIfStatementNotFollowedByElse()
{
const string document = @"if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
Debug.WriteLine(@""foo } bar"");
}";
SingleSpanBlockTest(document);
}
[Fact]
public void ParseBlockAcceptsElseIfWithNoCondition()
{
// We don't want to be a full C# parser - If the else if is missing it's condition, the C# compiler
// can handle that, we have all the info we need to keep parsing
const string ifBranch = @"if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
Debug.WriteLine(@""foo } bar"");
}";
const string elseIfBranch = @" else if { foo(); }";
const string document = ifBranch + elseIfBranch;
SingleSpanBlockTest(document);
}
[Fact]
public void ParseBlockCorrectlyParsesDoWhileBlock()
{
SingleSpanBlockTest(
"do { var foo = bar; } while(foo != bar);");
}
[Fact]
public void ParseBlockCorrectlyParsesDoWhileBlockMissingSemicolon()
{
SingleSpanBlockTest("do { var foo = bar; } while(foo != bar)");
}
[Fact]
public void ParseBlockCorrectlyParsesDoWhileBlockMissingWhileCondition()
{
SingleSpanBlockTest("do { var foo = bar; } while");
}
[Fact]
public void ParseBlockCorrectlyParsesDoWhileBlockMissingWhileConditionWithSemicolon()
{
SingleSpanBlockTest(
"do { var foo = bar; } while;");
}
[Fact]
public void ParseBlockCorrectlyParsesDoWhileBlockMissingWhileClauseEntirely()
{
SingleSpanBlockTest("do { var foo = bar; } narf;");
}
[Fact]
public void ParseBlockSupportsBlockCommentBetweenDoAndWhileClause()
{
SingleSpanBlockTest(
"do { var foo = bar; } /* Foo */ /* Bar */ while(true);");
}
[Fact]
public void ParseBlockSupportsLineCommentBetweenDoAndWhileClause()
{
SingleSpanBlockTest(@"do { var foo = bar; }
// Foo
// Bar
while(true);");
}
[Fact]
public void ParseBlockSupportsRazorCommentBetweenDoAndWhileClause()
{
RunRazorCommentBetweenClausesTest(
"do { var foo = bar; } ", " while(true);");
}
[Fact]
public void ParseBlockCorrectlyParsesMarkupInDoWhileBlock()
{
ParseBlockTest("@do { var foo = bar; <p>Foo</p> foo++; } while (foo<bar>);");
}
[Fact]
public void ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsSwitchKeyword()
{
SingleSpanBlockTest(@"switch(foo) {
case 0:
break;
case 1:
{
break;
}
case 2:
return;
default:
return;
}");
}
[Fact]
public void ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsLockKeyword()
{
SingleSpanBlockTest(
"lock(foo) { Debug.WriteLine(@\"foo } bar\"); }");
}
[Fact]
public void ParseBlockHasErrorsIfNamespaceImportMissingSemicolon()
{
ParseBlockTest(
"using Foo.Bar.Baz");
}
[Fact]
public void ParseBlockHasErrorsIfNamespaceAliasMissingSemicolon()
{
ParseBlockTest(
"using Foo.Bar.Baz = FooBarBaz");
}
[Fact]
public void ParseBlockParsesNamespaceImportWithSemicolonForUsingKeywordIfIsInValidFormat()
{
ParseBlockTest(
"using Foo.Bar.Baz;");
}
[Fact]
public void ParseBlockDoesntCaptureWhitespaceAfterUsing()
{
ParseBlockTest("using Foo ");
}
[Fact]
public void ParseBlockCapturesNewlineAfterUsing()
{
ParseBlockTest($"using Foo{Environment.NewLine}");
}
[Fact]
public void ParseBlockParsesNamespaceAliasWithSemicolonForUsingKeywordIfIsInValidFormat()
{
ParseBlockTest(
"using FooBarBaz = FooBarBaz;");
}
[Fact]
public void ParseBlockTerminatesUsingKeywordAtEOFAndOutputsFileCodeBlock()
{
SingleSpanBlockTest("using ");
}
[Fact]
public void ParseBlockTerminatesSingleLineCommentAtEndOfFile()
{
const string document = "foreach(var f in Foo) { // foo bar baz";
SingleSpanBlockTest(document);
}
[Fact]
public void ParseBlockTerminatesBlockCommentAtEndOfFile()
{
const string document = "foreach(var f in Foo) { /* foo bar baz";
SingleSpanBlockTest(document);
}
[Fact]
public void ParseBlockTerminatesSingleSlashAtEndOfFile()
{
const string document = "foreach(var f in Foo) { / foo bar baz";
SingleSpanBlockTest(document);
}
[Fact]
public void ParseBlockSupportsBlockCommentBetweenTryAndFinallyClause()
{
SingleSpanBlockTest("try { bar(); } /* Foo */ /* Bar */ finally { baz(); }");
}
[Fact]
public void ParseBlockSupportsRazorCommentBetweenTryAndFinallyClause()
{
RunRazorCommentBetweenClausesTest("try { bar(); } ", " finally { biz(); }");
}
[Fact]
public void ParseBlockSupportsBlockCommentBetweenCatchAndFinallyClause()
{
SingleSpanBlockTest(
"try { bar(); } catch(bar) { baz(); } /* Foo */ /* Bar */ finally { biz(); }");
}
[Fact]
public void ParseBlockSupportsRazorCommentBetweenCatchAndFinallyClause()
{
RunRazorCommentBetweenClausesTest(
"try { bar(); } catch(bar) { baz(); } ", " finally { biz(); }");
}
[Fact]
public void ParseBlockSupportsBlockCommentBetweenTryAndCatchClause()
{
SingleSpanBlockTest("try { bar(); } /* Foo */ /* Bar */ catch(bar) { baz(); }");
}
[Fact]
public void ParseBlockSupportsRazorCommentBetweenTryAndCatchClause()
{
RunRazorCommentBetweenClausesTest("try { bar(); }", " catch(bar) { baz(); }");
}
[Fact]
public void ParseBlockSupportsLineCommentBetweenTryAndFinallyClause()
{
SingleSpanBlockTest(@"try { bar(); }
// Foo
// Bar
finally { baz(); }");
}
[Fact]
public void ParseBlockSupportsLineCommentBetweenCatchAndFinallyClause()
{
SingleSpanBlockTest(@"try { bar(); } catch(bar) { baz(); }
// Foo
// Bar
finally { biz(); }");
}
[Fact]
public void ParseBlockSupportsLineCommentBetweenTryAndCatchClause()
{
SingleSpanBlockTest(@"try { bar(); }
// Foo
// Bar
catch(bar) { baz(); }");
}
[Fact]
public void ParseBlockSupportsTryStatementWithNoAdditionalClauses()
{
SingleSpanBlockTest("try { var foo = new { } }");
}
[Fact]
public void ParseBlockSupportsMarkupWithinTryClause()
{
RunSimpleWrappedMarkupTest(
prefix: "try {",
markup: " <p>Foo</p> ",
suffix: "}");
}
[Fact]
public void ParseBlockSupportsTryStatementWithOneCatchClause()
{
SingleSpanBlockTest("try { var foo = new { } } catch(Foo Bar Baz) { var foo = new { } }");
}
[Fact]
public void ParseBlockSupportsMarkupWithinCatchClause()
{
RunSimpleWrappedMarkupTest(
prefix: "try { var foo = new { } } catch(Foo Bar Baz) {",
markup: " <p>Foo</p> ",
suffix: "}");
}
[Fact]
public void ParseBlockSupportsTryStatementWithMultipleCatchClause()
{
SingleSpanBlockTest(
"try { var foo = new { } } catch(Foo Bar Baz) { var foo = new { } } catch(Foo Bar Baz) " +
"{ var foo = new { } } catch(Foo Bar Baz) { var foo = new { } }");
}
[Fact]
public void ParseBlockSupportsExceptionLessCatchClauses()
{
SingleSpanBlockTest("try { var foo = new { } } catch { var foo = new { } }");
}
[Fact]
public void ParseBlockSupportsMarkupWithinAdditionalCatchClauses()
{
RunSimpleWrappedMarkupTest(
prefix: "try { var foo = new { } } catch(Foo Bar Baz) { var foo = new { } } catch(Foo Bar Baz) " +
"{ var foo = new { } } catch(Foo Bar Baz) {",
markup: " <p>Foo</p> ",
suffix: "}");
}
[Fact]
public void ParseBlockSupportsTryStatementWithFinallyClause()
{
SingleSpanBlockTest("try { var foo = new { } } finally { var foo = new { } }");
}
[Fact]
public void ParseBlockSupportsMarkupWithinFinallyClause()
{
RunSimpleWrappedMarkupTest(
prefix: "try { var foo = new { } } finally {",
markup: " <p>Foo</p> ",
suffix: "}");
}
[Fact]
public void ParseBlockStopsParsingCatchClausesAfterFinallyBlock()
{
var content = "try { var foo = new { } } finally { var foo = new { } }";
SingleSpanBlockTest(content + " catch(Foo Bar Baz) { }");
}
[Fact]
public void ParseBlockDoesNotAllowMultipleFinallyBlocks()
{
var content = "try { var foo = new { } } finally { var foo = new { } }";
SingleSpanBlockTest(content + " finally { }");
}
[Fact]
public void ParseBlockAcceptsTrailingDotIntoImplicitExpressionWhenEmbeddedInCode()
{
// Arrange
ParseBlockTest(@"if(foo) { @foo. }");
}
[Fact]
public void ParseBlockParsesExpressionOnSwitchCharacterFollowedByOpenParen()
{
// Arrange
ParseBlockTest(@"if(foo) { @(foo + bar) }");
}
[Fact]
public void ParseBlockParsesExpressionOnSwitchCharacterFollowedByIdentifierStart()
{
// Arrange
ParseBlockTest(@"if(foo) { @foo[4].bar() }");
}
[Fact]
public void ParseBlockTreatsDoubleAtSignAsEscapeSequenceIfAtStatementStart()
{
// Arrange
ParseBlockTest(@"if(foo) { @@class.Foo() }");
}
[Fact]
public void ParseBlockTreatsAtSignsAfterFirstPairAsPartOfCSharpStatement()
{
// Arrange
ParseBlockTest(@"if(foo) { @@@@class.Foo() }");
}
[Fact]
public void ParseBlockDoesNotParseMarkupStatementOrExpressionOnSwitchCharacterNotFollowedByOpenAngleOrColon()
{
// Arrange
ParseBlockTest("if(foo) { @\"Foo\".ToString(); }");
}
[Fact]
public void ParsersCanNestRecursively()
{
// Arrange
ParseBlockTest("foreach(var c in db.Categories) {" + Environment.NewLine
+ " <div>" + Environment.NewLine
+ " <h1>@c.Name</h1>" + Environment.NewLine
+ " <ul>" + Environment.NewLine
+ " @foreach(var p in c.Products) {" + Environment.NewLine
+ " <li><a href=\"@Html.ActionUrl(\"Products\", \"Detail\", new { id = p.Id })\">@p.Name</a></li>" + Environment.NewLine
+ " }" + Environment.NewLine
+ " </ul>" + Environment.NewLine
+ " </div>" + Environment.NewLine
+ " }");
}
[Fact]
public void ParseBlock_WithDoubleTransitionInAttributeValue_DoesNotThrow()
{
var input = "{<span foo='@@' />}";
ParseBlockTest(input);
}
[Fact]
public void ParseBlock_WithDoubleTransitionAtEndOfAttributeValue_DoesNotThrow()
{
var input = "{<span foo='abc@@' />}";
ParseBlockTest(input);
}
[Fact]
public void ParseBlock_WithDoubleTransitionAtBeginningOfAttributeValue_DoesNotThrow()
{
var input = "{<span foo='@@def' />}";
ParseBlockTest(input);
}
[Fact]
public void ParseBlock_WithDoubleTransitionBetweenAttributeValue_DoesNotThrow()
{
var input = "{<span foo='abc @@ def' />}";
ParseBlockTest(input);
}
[Fact]
public void ParseBlock_WithDoubleTransitionWithExpressionBlock_DoesNotThrow()
{
var input = "{<span foo='@@@(2+3)' bar='@(2+3)@@@DateTime.Now' baz='@DateTime.Now@@' bat='@DateTime.Now @@' zoo='@@@DateTime.Now' />}";
ParseBlockTest(input);
}
[Fact]
public void ParseBlock_WithDoubleTransitionInEmail_DoesNotThrow()
{
var input = "{<span foo='abc@def.com abc@@def.com @@' />}";
ParseBlockTest(input);
}
[Fact]
public void ParseBlock_WithDoubleTransitionInRegex_DoesNotThrow()
{
var input = @"{<span foo=""/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@@[a-z0-9]([a-z0-9-]*[a-z0-9])?\.([a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i"" />}";
ParseBlockTest(input);
}
[Fact]
public void ParseBlock_WithDoubleTransition_EndOfFile_Throws()
{
ParseBlockTest("{<span foo='@@");
}
[Fact]
public void ParseBlock_WithUnexpectedTransitionsInAttributeValue_Throws()
{
ParseBlockTest("{<span foo='@ @' />}");
}
private void RunRazorCommentBetweenClausesTest(string preComment, string postComment, AcceptedCharactersInternal acceptedCharacters = AcceptedCharactersInternal.Any)
{
ParseBlockTest(preComment + "@* Foo *@ @* Bar *@" + postComment);
}
private void RunSimpleWrappedMarkupTest(string prefix, string markup, string suffix)
{
ParseBlockTest(prefix + markup + suffix);
}
}
}