// 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.Linq;
using Microsoft.AspNetCore.Razor.Language.Extensions;
using Xunit;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
public class HtmlDocumentTest : CsHtmlMarkupParserTestBase
{
private static readonly TestFile Nested1000 = TestFile.Create("TestFiles/nested-1000.html", typeof(HtmlDocumentTest));
[Fact]
public void ParseDocument_NestedCodeBlockWithMarkupSetsDotAsMarkup()
{
ParseDocumentTest("@if (true) { @if(false) {
@something.
} }",
new MarkupBlock(
Factory.EmptyHtml(),
new StatementBlock(
Factory.CodeTransition(),
Factory.Code("if (true) { ").AsStatement(),
new StatementBlock(
Factory.CodeTransition(),
Factory.Code("if(false) {").AsStatement(),
new MarkupBlock(
Factory.Markup(" "),
BlockFactory.MarkupTagBlock("", AcceptedCharactersInternal.None),
Factory.EmptyHtml(),
new ExpressionBlock(
Factory.CodeTransition(),
Factory.Code("something")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: false)
.Accepts(AcceptedCharactersInternal.NonWhiteSpace)),
Factory.Markup("."),
BlockFactory.MarkupTagBlock("
", AcceptedCharactersInternal.None),
Factory.Markup(" ").Accepts(AcceptedCharactersInternal.None)),
Factory.Code("}").AsStatement()),
Factory.Code(" }").AsStatement())));
}
[Fact]
public void ParseDocumentOutputsEmptyBlockWithEmptyMarkupSpanIfContentIsEmptyString()
{
ParseDocumentTest(string.Empty, new MarkupBlock(Factory.EmptyHtml()));
}
[Fact]
public void ParseDocumentOutputsWhitespaceOnlyContentAsSingleWhitespaceMarkupSpan()
{
SingleSpanDocumentTest(" ", BlockKindInternal.Markup, SpanKindInternal.Markup);
}
[Fact]
public void ParseDocumentAcceptsSwapTokenAtEndOfFileAndOutputsZeroLengthCodeSpan()
{
ParseDocumentTest("@",
new MarkupBlock(
Factory.EmptyHtml(),
new ExpressionBlock(
Factory.CodeTransition(),
Factory.EmptyCSharp()
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharactersInternal.NonWhiteSpace)),
Factory.EmptyHtml()),
RazorDiagnosticFactory.CreateParsing_UnexpectedEndOfFileAtStartOfCodeBlock(
new SourceSpan(new SourceLocation(1, 0, 1), contentLength: 1)));
}
[Fact]
public void ParseDocumentCorrectlyHandlesOddlySpacedHTMLElements()
{
ParseDocumentTest("",
new MarkupBlock(
BlockFactory.MarkupTagBlock(""),
new MarkupTagBlock(
Factory.Markup("
(" class = '", 8, 0, 8), suffix: new LocationTagged("'", 21, 0, 21)),
Factory.Markup(" class = '").With(SpanChunkGenerator.Null),
Factory.Markup("bar").With(new LiteralAttributeChunkGenerator(prefix: new LocationTagged(string.Empty, 18, 0, 18), value: new LocationTagged("bar", 18, 0, 18))),
Factory.Markup("'").With(SpanChunkGenerator.Null)),
Factory.Markup(">")),
Factory.Markup(" Foo "),
BlockFactory.MarkupTagBlock("
"),
BlockFactory.MarkupTagBlock("
")));
}
[Fact]
public void ParseDocumentCorrectlyHandlesSingleLineOfMarkupWithEmbeddedStatement()
{
ParseDocumentTest("Foo @if(true) {} Bar
",
new MarkupBlock(
BlockFactory.MarkupTagBlock(""),
Factory.Markup("Foo "),
new StatementBlock(
Factory.CodeTransition(),
Factory.Code("if(true) {}").AsStatement()),
Factory.Markup(" Bar"),
BlockFactory.MarkupTagBlock("
")));
}
[Fact]
public void ParseDocumentWithinSectionDoesNotCreateDocumentLevelSpan()
{
ParseDocumentTest("@section Foo {" + Environment.NewLine
+ " " + Environment.NewLine
+ "}",
new[] { SectionDirective.Directive, },
new MarkupBlock(
Factory.EmptyHtml(),
new DirectiveBlock(new DirectiveChunkGenerator(SectionDirective.Directive),
Factory.CodeTransition(),
Factory.MetaCode("section").Accepts(AcceptedCharactersInternal.None),
Factory.Span(SpanKindInternal.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.WhiteSpace),
Factory.Span(SpanKindInternal.Code, "Foo", CSharpSymbolType.Identifier).AsDirectiveToken(SectionDirective.Directive.Tokens[0]),
Factory.Span(SpanKindInternal.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.AllWhiteSpace),
Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharactersInternal.None),
new MarkupBlock(
Factory.Markup(Environment.NewLine + " "),
BlockFactory.MarkupTagBlock(""),
BlockFactory.MarkupTagBlock(""),
Factory.Markup(Environment.NewLine)),
Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)),
Factory.EmptyHtml()));
}
[Fact]
public void ParseDocumentParsesWholeContentAsOneSpanIfNoSwapCharacterEncountered()
{
SingleSpanDocumentTest("foo baz", BlockKindInternal.Markup, SpanKindInternal.Markup);
}
[Fact]
public void ParseDocumentHandsParsingOverToCodeParserWhenAtSignEncounteredAndEmitsOutput()
{
ParseDocumentTest("foo @bar baz",
new MarkupBlock(
Factory.Markup("foo "),
new ExpressionBlock(
Factory.CodeTransition(),
Factory.Code("bar")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharactersInternal.NonWhiteSpace)),
Factory.Markup(" baz")));
}
[Fact]
public void ParseDocumentEmitsAtSignAsMarkupIfAtEndOfFile()
{
ParseDocumentTest("foo @",
new MarkupBlock(
Factory.Markup("foo "),
new ExpressionBlock(
Factory.CodeTransition(),
Factory.EmptyCSharp()
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharactersInternal.NonWhiteSpace)),
Factory.EmptyHtml()),
RazorDiagnosticFactory.CreateParsing_UnexpectedEndOfFileAtStartOfCodeBlock(
new SourceSpan(new SourceLocation(5, 0, 5), contentLength: 1)));
}
[Fact]
public void ParseDocumentEmitsCodeBlockIfFirstCharacterIsSwapCharacter()
{
ParseDocumentTest("@bar",
new MarkupBlock(
Factory.EmptyHtml(),
new ExpressionBlock(
Factory.CodeTransition(),
Factory.Code("bar")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharactersInternal.NonWhiteSpace)),
Factory.EmptyHtml()));
}
[Fact]
public void ParseDocumentDoesNotSwitchToCodeOnEmailAddressInText()
{
SingleSpanDocumentTest("anurse@microsoft.com", BlockKindInternal.Markup, SpanKindInternal.Markup);
}
[Fact]
public void ParseDocumentDoesNotSwitchToCodeOnEmailAddressInAttribute()
{
ParseDocumentTest("Email me",
new MarkupBlock(
new MarkupTagBlock(
Factory.Markup("(" href=\"", 2, 0, 2), new LocationTagged("\"", 36, 0, 36)),
Factory.Markup(" href=\"").With(SpanChunkGenerator.Null),
Factory.Markup("mailto:anurse@microsoft.com")
.With(new LiteralAttributeChunkGenerator(new LocationTagged(string.Empty, 9, 0, 9), new LocationTagged("mailto:anurse@microsoft.com", 9, 0, 9))),
Factory.Markup("\"").With(SpanChunkGenerator.Null)),
Factory.Markup(">")),
Factory.Markup("Email me"),
BlockFactory.MarkupTagBlock("")));
}
[Fact]
public void ParseDocumentDoesNotReturnErrorOnMismatchedTags()
{
ParseDocumentTest("Foo Baz",
new MarkupBlock(
Factory.Markup("Foo "),
BlockFactory.MarkupTagBlock("
"),
BlockFactory.MarkupTagBlock("
"),
BlockFactory.MarkupTagBlock("
"),
BlockFactory.MarkupTagBlock(""),
Factory.Markup(" Baz")));
}
[Fact]
public void ParseDocumentReturnsOneMarkupSegmentIfNoCodeBlocksEncountered()
{
ParseDocumentTest("Foo BazBar