// 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("

Foo

", 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