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

247 lines
13 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 Microsoft.AspNetCore.Razor.Language.Extensions;
using Xunit;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
public class CSharpSpecialBlockTest : CsHtmlCodeParserTestBase
{
[Fact]
public void ParseInheritsStatementMarksInheritsSpanAsCanGrowIfMissingTrailingSpace()
{
// Arrange
var chunkGenerator = new DirectiveChunkGenerator(InheritsDirective.Directive);
chunkGenerator.Diagnostics.Add(
RazorDiagnosticFactory.CreateParsing_UnexpectedEOFAfterDirective(
new SourceSpan(new SourceLocation(9, 0, 9), 1), InheritsDirective.Directive.Directive, "type"));
// Act & Assert
ParseDocumentTest(
"@inherits",
new[] { InheritsDirective.Directive },
new MarkupBlock(
Factory.EmptyHtml(),
new DirectiveBlock(chunkGenerator,
Factory.CodeTransition(),
Factory.MetaCode("inherits").Accepts(AcceptedCharactersInternal.None),
Factory.Span(SpanKindInternal.Code, string.Empty, CSharpSymbolType.Unknown)
.AsDirectiveToken(DirectiveTokenDescriptor.CreateToken(DirectiveTokenKind.Type))),
Factory.EmptyHtml()));
}
[Fact]
public void InheritsBlockAcceptsMultipleGenericArguments()
{
ParseDocumentTest(
"@inherits Foo.Bar<Biz<Qux>, string, int>.Baz",
new[] { InheritsDirective.Directive },
new MarkupBlock(
Factory.EmptyHtml(),
new DirectiveBlock(new DirectiveChunkGenerator(InheritsDirective.Directive),
Factory.CodeTransition(),
Factory.MetaCode("inherits").Accepts(AcceptedCharactersInternal.None),
Factory.Span(SpanKindInternal.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.WhiteSpace),
Factory.Span(SpanKindInternal.Code, "Foo.Bar<Biz<Qux>, string, int>.Baz", markup: false).AsDirectiveToken(InheritsDirective.Directive.Tokens[0])),
Factory.EmptyHtml()));
}
[Fact]
public void InheritsBlockOutputsErrorIfInheritsNotFollowedByTypeButAcceptsEntireLineAsCode()
{
// Arrange
var chunkGenerator = new DirectiveChunkGenerator(InheritsDirective.Directive);
chunkGenerator.Diagnostics.Add(
RazorDiagnosticFactory.CreateParsing_DirectiveExpectsTypeName(
new SourceSpan(new SourceLocation(25, 0, 25), Environment.NewLine.Length),
InheritsDirective.Directive.Directive));
// Act & Assert
ParseDocumentTest(
"@inherits " + Environment.NewLine + "foo",
new[] { InheritsDirective.Directive },
new MarkupBlock(
Factory.EmptyHtml(),
new DirectiveBlock(chunkGenerator,
Factory.CodeTransition(),
Factory.MetaCode("inherits").Accepts(AcceptedCharactersInternal.None),
Factory.Span(SpanKindInternal.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.WhiteSpace),
Factory.Span(SpanKindInternal.Code, string.Empty, CSharpSymbolType.Unknown)
.AsDirectiveToken(DirectiveTokenDescriptor.CreateToken(DirectiveTokenKind.Type))),
Factory.Markup(Environment.NewLine + "foo")));
}
[Fact]
public void NamespaceImportInsideCodeBlockCausesError()
{
ParseBlockTest("{ using Foo.Bar.Baz; var foo = bar; }",
new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharactersInternal.None),
Factory.Code(" using Foo.Bar.Baz; var foo = bar; ")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)
),
RazorDiagnosticFactory.CreateParsing_NamespaceImportAndTypeAliasCannotExistWithinCodeBlock(
new SourceSpan(new SourceLocation(2, 0, 2), contentLength: 5)));
}
[Fact]
public void TypeAliasInsideCodeBlockIsNotHandledSpecially()
{
ParseBlockTest("{ using Foo = Bar.Baz; var foo = bar; }",
new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharactersInternal.None),
Factory.Code(" using Foo = Bar.Baz; var foo = bar; ")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)
),
RazorDiagnosticFactory.CreateParsing_NamespaceImportAndTypeAliasCannotExistWithinCodeBlock(
new SourceSpan(new SourceLocation(2, 0, 2), contentLength: 5)));
}
[Fact]
public void Plan9FunctionsKeywordInsideCodeBlockIsNotHandledSpecially()
{
ParseBlockTest("{ functions Foo; }",
new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharactersInternal.None),
Factory.Code(" functions Foo; ")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)
));
}
[Fact]
public void NonKeywordStatementInCodeBlockIsHandledCorrectly()
{
ParseBlockTest("{" + Environment.NewLine
+ " List<dynamic> photos = gallery.Photo.ToList();" + Environment.NewLine
+ "}",
new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharactersInternal.None),
Factory.Code($"{Environment.NewLine} List<dynamic> photos = gallery.Photo.ToList();{Environment.NewLine}")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)
));
}
[Fact]
public void ParseBlockBalancesBracesOutsideStringsIfFirstCharacterIsBraceAndReturnsSpanOfTypeCode()
{
// Arrange
const string code = "foo\"b}ar\" if(condition) { string.Format(\"{0}\"); } ";
// Act/Assert
ParseBlockTest("{" + code + "}",
new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharactersInternal.None),
Factory.Code(code)
.AsStatement()
.AutoCompleteWith(autoCompleteString: null),
Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)
));
}
[Fact]
public void ParseBlockBalancesParensOutsideStringsIfFirstCharacterIsParenAndReturnsSpanOfTypeExpression()
{
// Arrange
const string code = "foo\"b)ar\" if(condition) { string.Format(\"{0}\"); } ";
// Act/Assert
ParseBlockTest("(" + code + ")",
new ExpressionBlock(
Factory.MetaCode("(").Accepts(AcceptedCharactersInternal.None),
Factory.Code(code).AsExpression(),
Factory.MetaCode(")").Accepts(AcceptedCharactersInternal.None)
));
}
[Fact]
public void ParseBlockBalancesBracesAndOutputsContentAsClassLevelCodeSpanIfFirstIdentifierIsFunctionsKeyword()
{
const string code = " foo(); \"bar}baz\" ";
ParseBlockTest(
"functions {" + code + "} zoop",
new[] { FunctionsDirective.Directive },
new DirectiveBlock(new DirectiveChunkGenerator(FunctionsDirective.Directive),
Factory.MetaCode("functions").Accepts(AcceptedCharactersInternal.None),
Factory.Span(SpanKindInternal.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.AllWhiteSpace),
Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharactersInternal.None),
Factory.Code(code).AsStatement(),
Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)));
}
[Fact]
public void ParseBlockDoesNoErrorRecoveryForFunctionsBlock()
{
// Arrange
var chunkGenerator = new DirectiveChunkGenerator(FunctionsDirective.Directive);
chunkGenerator.Diagnostics.Add(
RazorDiagnosticFactory.CreateParsing_ExpectedEndOfBlockBeforeEOF(
new SourceSpan(new SourceLocation(10, 0, 10), contentLength: 1), "functions", "}", "{"));
// Act & Assert
ParseBlockTest(
"functions { { { { { } zoop",
new[] { FunctionsDirective.Directive },
new DirectiveBlock(chunkGenerator,
Factory.MetaCode("functions").Accepts(AcceptedCharactersInternal.None),
Factory.Span(SpanKindInternal.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.AllWhiteSpace),
Factory.MetaCode("{").AutoCompleteWith("}", atEndOfSpan: true).Accepts(AcceptedCharactersInternal.None),
Factory.Code(" { { { { } zoop").AsStatement()));
}
[Fact]
public void ParseBlockIgnoresFunctionsUnlessAllLowerCase()
{
ParseBlockTest("Functions { foo() }",
new ExpressionBlock(
Factory.Code("Functions")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharactersInternal.NonWhiteSpace)));
}
[Fact]
public void ParseBlockIgnoresSingleSlashAtStart()
{
ParseBlockTest("@/ foo",
new ExpressionBlock(
Factory.CodeTransition(),
Factory.EmptyCSharp()
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharactersInternal.NonWhiteSpace)),
RazorDiagnosticFactory.CreateParsing_UnexpectedCharacterAtStartOfCodeBlock(
new SourceSpan(new SourceLocation(1, 0, 1), contentLength: 1),
"/"));
}
[Fact]
public void ParseBlockTerminatesSingleLineCommentAtEndOfLine()
{
ParseBlockTest("if(!false) {" + Environment.NewLine
+ " // Foo" + Environment.NewLine
+ "\t<p>A real tag!</p>" + Environment.NewLine
+ "}",
new StatementBlock(
Factory.Code($"if(!false) {{{Environment.NewLine} // Foo{Environment.NewLine}").AsStatement(),
new MarkupBlock(
Factory.Markup("\t"),
new MarkupTagBlock(
Factory.Markup("<p>").Accepts(AcceptedCharactersInternal.None)),
Factory.Markup("A real tag!"),
new MarkupTagBlock(
Factory.Markup("</p>").Accepts(AcceptedCharactersInternal.None)),
Factory.Markup(Environment.NewLine).Accepts(AcceptedCharactersInternal.None)),
Factory.Code("}").AsStatement()
));
}
}
}