804 lines
48 KiB
C#
804 lines
48 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 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) { <div>@something.</div> } }",
|
|
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("<div>", AcceptedCharactersInternal.None),
|
|
Factory.EmptyHtml(),
|
|
new ExpressionBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.Code("something")
|
|
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: false)
|
|
.Accepts(AcceptedCharactersInternal.NonWhiteSpace)),
|
|
Factory.Markup("."),
|
|
BlockFactory.MarkupTagBlock("</div>", 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("<div ><p class = 'bar'> Foo </p></div >",
|
|
new MarkupBlock(
|
|
BlockFactory.MarkupTagBlock("<div >"),
|
|
new MarkupTagBlock(
|
|
Factory.Markup("<p"),
|
|
new MarkupBlock(new AttributeBlockChunkGenerator(name: "class", prefix: new LocationTagged<string>(" class = '", 8, 0, 8), suffix: new LocationTagged<string>("'", 21, 0, 21)),
|
|
Factory.Markup(" class = '").With(SpanChunkGenerator.Null),
|
|
Factory.Markup("bar").With(new LiteralAttributeChunkGenerator(prefix: new LocationTagged<string>(string.Empty, 18, 0, 18), value: new LocationTagged<string>("bar", 18, 0, 18))),
|
|
Factory.Markup("'").With(SpanChunkGenerator.Null)),
|
|
Factory.Markup(">")),
|
|
Factory.Markup(" Foo "),
|
|
BlockFactory.MarkupTagBlock("</p>"),
|
|
BlockFactory.MarkupTagBlock("</div >")));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseDocumentCorrectlyHandlesSingleLineOfMarkupWithEmbeddedStatement()
|
|
{
|
|
ParseDocumentTest("<div>Foo @if(true) {} Bar</div>",
|
|
new MarkupBlock(
|
|
BlockFactory.MarkupTagBlock("<div>"),
|
|
Factory.Markup("Foo "),
|
|
new StatementBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.Code("if(true) {}").AsStatement()),
|
|
Factory.Markup(" Bar"),
|
|
BlockFactory.MarkupTagBlock("</div>")));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseDocumentWithinSectionDoesNotCreateDocumentLevelSpan()
|
|
{
|
|
ParseDocumentTest("@section Foo {" + Environment.NewLine
|
|
+ " <html></html>" + 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("<html>"),
|
|
BlockFactory.MarkupTagBlock("</html>"),
|
|
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("<a href=\"mailto:anurse@microsoft.com\">Email me</a>",
|
|
new MarkupBlock(
|
|
new MarkupTagBlock(
|
|
Factory.Markup("<a"),
|
|
new MarkupBlock(new AttributeBlockChunkGenerator("href", new LocationTagged<string>(" href=\"", 2, 0, 2), new LocationTagged<string>("\"", 36, 0, 36)),
|
|
Factory.Markup(" href=\"").With(SpanChunkGenerator.Null),
|
|
Factory.Markup("mailto:anurse@microsoft.com")
|
|
.With(new LiteralAttributeChunkGenerator(new LocationTagged<string>(string.Empty, 9, 0, 9), new LocationTagged<string>("mailto:anurse@microsoft.com", 9, 0, 9))),
|
|
Factory.Markup("\"").With(SpanChunkGenerator.Null)),
|
|
Factory.Markup(">")),
|
|
Factory.Markup("Email me"),
|
|
BlockFactory.MarkupTagBlock("</a>")));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseDocumentDoesNotReturnErrorOnMismatchedTags()
|
|
{
|
|
ParseDocumentTest("Foo <div><p></p></p> Baz",
|
|
new MarkupBlock(
|
|
Factory.Markup("Foo "),
|
|
BlockFactory.MarkupTagBlock("<div>"),
|
|
BlockFactory.MarkupTagBlock("<p>"),
|
|
BlockFactory.MarkupTagBlock("</p>"),
|
|
BlockFactory.MarkupTagBlock("</p>"),
|
|
Factory.Markup(" Baz")));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseDocumentReturnsOneMarkupSegmentIfNoCodeBlocksEncountered()
|
|
{
|
|
ParseDocumentTest("Foo Baz<!--Foo-->Bar<!--F> Qux",
|
|
new MarkupBlock(
|
|
Factory.Markup("Foo Baz"),
|
|
BlockFactory.HtmlCommentBlock("Foo"),
|
|
Factory.Markup("Bar"),
|
|
Factory.Markup("<!--F> Qux")));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseDocumentRendersTextPseudoTagAsMarkup()
|
|
{
|
|
ParseDocumentTest("Foo <text>Foo</text>",
|
|
new MarkupBlock(
|
|
Factory.Markup("Foo "),
|
|
BlockFactory.MarkupTagBlock("<text>"),
|
|
Factory.Markup("Foo"),
|
|
BlockFactory.MarkupTagBlock("</text>")));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseDocumentAcceptsEndTagWithNoMatchingStartTag()
|
|
{
|
|
ParseDocumentTest("Foo </div> Bar",
|
|
new MarkupBlock(
|
|
Factory.Markup("Foo "),
|
|
BlockFactory.MarkupTagBlock("</div>"),
|
|
Factory.Markup(" Bar")));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseDocumentNoLongerSupportsDollarOpenBraceCombination()
|
|
{
|
|
ParseDocumentTest("<foo>${bar}</foo>",
|
|
new MarkupBlock(
|
|
BlockFactory.MarkupTagBlock("<foo>"),
|
|
Factory.Markup("${bar}"),
|
|
BlockFactory.MarkupTagBlock("</foo>")));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseDocumentIgnoresTagsInContentsOfScriptTag()
|
|
{
|
|
ParseDocumentTest(@"<script>foo<bar baz='@boz'></script>",
|
|
new MarkupBlock(
|
|
BlockFactory.MarkupTagBlock("<script>"),
|
|
Factory.Markup("foo<bar baz='"),
|
|
new ExpressionBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.Code("boz")
|
|
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: false)
|
|
.Accepts(AcceptedCharactersInternal.NonWhiteSpace)),
|
|
Factory.Markup("'>"),
|
|
BlockFactory.MarkupTagBlock("</script>")));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseDocumentDoesNotRenderExtraNewLineAtTheEndOfVerbatimBlock()
|
|
{
|
|
ParseDocumentTest("@{\r\n}\r\n<html>",
|
|
new MarkupBlock(
|
|
Factory.EmptyHtml(),
|
|
new StatementBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.MetaCode("{").Accepts(AcceptedCharactersInternal.None),
|
|
Factory.Code("\r\n").AsStatement().AutoCompleteWith(null, false),
|
|
Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)),
|
|
Factory.Markup("\r\n").With(SpanChunkGenerator.Null),
|
|
BlockFactory.MarkupTagBlock("<html>")));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseDocumentDoesNotRenderExtraWhitespaceAndNewLineAtTheEndOfVerbatimBlock()
|
|
{
|
|
ParseDocumentTest("@{\r\n} \t\r\n<html>",
|
|
new MarkupBlock(
|
|
Factory.EmptyHtml(),
|
|
new StatementBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.MetaCode("{").Accepts(AcceptedCharactersInternal.None),
|
|
Factory.Code("\r\n").AsStatement().AutoCompleteWith(null, false),
|
|
Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)),
|
|
Factory.Markup(" \t\r\n").With(SpanChunkGenerator.Null),
|
|
BlockFactory.MarkupTagBlock("<html>")));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseDocumentDoesNotRenderExtraNewlineAtTheEndTextTagInVerbatimBlockIfFollowedByCSharp()
|
|
{
|
|
ParseDocumentTest("@{<text>Blah</text>\r\n\r\n}<html>",
|
|
new MarkupBlock(
|
|
Factory.EmptyHtml(),
|
|
new StatementBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.MetaCode("{").Accepts(AcceptedCharactersInternal.None),
|
|
new MarkupBlock(
|
|
new MarkupTagBlock(
|
|
Factory.MarkupTransition("<text>")),
|
|
Factory.Markup("Blah").Accepts(AcceptedCharactersInternal.None),
|
|
new MarkupTagBlock(
|
|
Factory.MarkupTransition("</text>"))),
|
|
Factory.Code("\r\n\r\n").AsStatement(),
|
|
Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)),
|
|
BlockFactory.MarkupTagBlock("<html>")));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseDocumentRendersExtraNewlineAtTheEndTextTagInVerbatimBlockIfFollowedByHtml()
|
|
{
|
|
ParseDocumentTest("@{<text>Blah</text>\r\n<input/>\r\n}<html>",
|
|
new MarkupBlock(
|
|
Factory.EmptyHtml(),
|
|
new StatementBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.MetaCode("{").Accepts(AcceptedCharactersInternal.None),
|
|
new MarkupBlock(
|
|
new MarkupTagBlock(
|
|
Factory.MarkupTransition("<text>")),
|
|
Factory.Markup("Blah").Accepts(AcceptedCharactersInternal.None),
|
|
new MarkupTagBlock(
|
|
Factory.MarkupTransition("</text>")),
|
|
Factory.Markup("\r\n").Accepts(AcceptedCharactersInternal.None)),
|
|
new MarkupBlock(
|
|
new MarkupTagBlock(
|
|
Factory.Markup("<input/>").Accepts(AcceptedCharactersInternal.None)),
|
|
Factory.Markup("\r\n").Accepts(AcceptedCharactersInternal.None)),
|
|
Factory.EmptyCSharp().AsStatement(),
|
|
Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)),
|
|
BlockFactory.MarkupTagBlock("<html>")));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseDocumentRendersExtraNewlineAtTheEndTextTagInVerbatimBlockIfFollowedByMarkupTransition()
|
|
{
|
|
ParseDocumentTest("@{<text>Blah</text>\r\n@: Bleh\r\n}<html>",
|
|
new MarkupBlock(
|
|
Factory.EmptyHtml(),
|
|
new StatementBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.MetaCode("{").Accepts(AcceptedCharactersInternal.None),
|
|
new MarkupBlock(
|
|
new MarkupTagBlock(
|
|
Factory.MarkupTransition("<text>")),
|
|
Factory.Markup("Blah").Accepts(AcceptedCharactersInternal.None),
|
|
new MarkupTagBlock(
|
|
Factory.MarkupTransition("</text>")),
|
|
Factory.Markup("\r\n").Accepts(AcceptedCharactersInternal.None)),
|
|
new MarkupBlock(
|
|
Factory.MarkupTransition(),
|
|
Factory.MetaMarkup(":", HtmlSymbolType.Colon),
|
|
Factory.Markup(" Bleh\r\n")
|
|
.With(new SpanEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString))
|
|
.Accepts(AcceptedCharactersInternal.None)),
|
|
Factory.EmptyCSharp().AsStatement(),
|
|
Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)),
|
|
BlockFactory.MarkupTagBlock("<html>")));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseDocumentDoesNotIgnoreNewLineAtTheEndOfMarkupBlock()
|
|
{
|
|
ParseDocumentTest("@{\r\n}\r\n<html>\r\n",
|
|
new MarkupBlock(
|
|
Factory.EmptyHtml(),
|
|
new StatementBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.MetaCode("{").Accepts(AcceptedCharactersInternal.None),
|
|
Factory.Code("\r\n").AsStatement().AutoCompleteWith(null, false),
|
|
Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)),
|
|
Factory.Markup("\r\n").With(SpanChunkGenerator.Null),
|
|
BlockFactory.MarkupTagBlock("<html>"),
|
|
Factory.Markup("\r\n")));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseDocumentDoesNotIgnoreWhitespaceAtTheEndOfVerbatimBlockIfNoNewlinePresent()
|
|
{
|
|
ParseDocumentTest("@{\r\n} \t<html>\r\n",
|
|
new MarkupBlock(
|
|
Factory.EmptyHtml(),
|
|
new StatementBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.MetaCode("{").Accepts(AcceptedCharactersInternal.None),
|
|
Factory.Code("\r\n").AsStatement().AutoCompleteWith(null, false),
|
|
Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)),
|
|
Factory.Markup(" \t"),
|
|
BlockFactory.MarkupTagBlock("<html>"),
|
|
Factory.Markup("\r\n")));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseDocumentHandlesNewLineInNestedBlock()
|
|
{
|
|
ParseDocumentTest("@{\r\n@if(true){\r\n} \r\n}\r\n<html>",
|
|
new MarkupBlock(
|
|
Factory.EmptyHtml(),
|
|
new StatementBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.MetaCode("{").Accepts(AcceptedCharactersInternal.None),
|
|
Factory.Code("\r\n").AsStatement().AutoCompleteWith(null, false),
|
|
new StatementBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.Code("if(true){\r\n}").AsStatement()),
|
|
Factory.Code(" \r\n").AsStatement(),
|
|
Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)),
|
|
Factory.Markup("\r\n").With(SpanChunkGenerator.Null),
|
|
BlockFactory.MarkupTagBlock("<html>")));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseDocumentHandlesNewLineAndMarkupInNestedBlock()
|
|
{
|
|
ParseDocumentTest("@{\r\n@if(true){\r\n} <input> }",
|
|
new MarkupBlock(
|
|
Factory.EmptyHtml(),
|
|
new StatementBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.MetaCode("{").Accepts(AcceptedCharactersInternal.None),
|
|
Factory.Code("\r\n").AsStatement().AutoCompleteWith(null, false),
|
|
new StatementBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.Code("if(true){\r\n}").AsStatement()),
|
|
new MarkupBlock(
|
|
Factory.Markup(" "),
|
|
new MarkupTagBlock(
|
|
Factory.Markup("<input>").Accepts(AcceptedCharactersInternal.None)),
|
|
Factory.Markup(" ").Accepts(AcceptedCharactersInternal.None)),
|
|
Factory.EmptyCSharp().AsStatement(),
|
|
Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)),
|
|
Factory.EmptyHtml()));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseDocumentHandlesExtraNewLineBeforeMarkupInNestedBlock()
|
|
{
|
|
ParseDocumentTest("@{\r\n@if(true){\r\n} \r\n<input> \r\n}<html>",
|
|
new MarkupBlock(
|
|
Factory.EmptyHtml(),
|
|
new StatementBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.MetaCode("{").Accepts(AcceptedCharactersInternal.None),
|
|
Factory.Code("\r\n").AsStatement().AutoCompleteWith(null, false),
|
|
new StatementBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.Code("if(true){\r\n}").AsStatement()),
|
|
Factory.Code(" \r\n").AsStatement(),
|
|
new MarkupBlock(
|
|
new MarkupTagBlock(
|
|
Factory.Markup("<input>").Accepts(AcceptedCharactersInternal.None)),
|
|
Factory.Markup(" \r\n").Accepts(AcceptedCharactersInternal.None)),
|
|
Factory.EmptyCSharp().AsStatement(),
|
|
Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)),
|
|
new MarkupTagBlock(
|
|
Factory.Markup("<html>"))));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseSectionIgnoresTagsInContentsOfScriptTag()
|
|
{
|
|
ParseDocumentTest(
|
|
@"@section Foo { <script>foo<bar baz='@boz'></script> }",
|
|
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(" "),
|
|
BlockFactory.MarkupTagBlock("<script>"),
|
|
Factory.Markup("foo<bar baz='"),
|
|
new ExpressionBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.Code("boz")
|
|
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: false)
|
|
.Accepts(AcceptedCharactersInternal.NonWhiteSpace)),
|
|
Factory.Markup("'>"),
|
|
BlockFactory.MarkupTagBlock("</script>"),
|
|
Factory.Markup(" ")),
|
|
Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)),
|
|
Factory.EmptyHtml()));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseBlockCanParse1000NestedElements()
|
|
{
|
|
var content = Nested1000.ReadAllText();
|
|
ParseDocument(content);
|
|
}
|
|
|
|
public static TheoryData BlockWithEscapedTransitionData
|
|
{
|
|
get
|
|
{
|
|
var factory = new SpanFactory();
|
|
var datetimeBlock = new ExpressionBlock(
|
|
factory.CodeTransition(),
|
|
factory.Code("DateTime.Now")
|
|
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
|
|
.Accepts(AcceptedCharactersInternal.NonWhiteSpace));
|
|
|
|
return new TheoryData<string, Block>
|
|
{
|
|
{
|
|
// Double transition in attribute value
|
|
"<span foo='@@' />",
|
|
new MarkupBlock(
|
|
new MarkupTagBlock(
|
|
factory.Markup("<span"),
|
|
new MarkupBlock(
|
|
new AttributeBlockChunkGenerator("foo", new LocationTagged<string>(" foo='", 5, 0, 5), new LocationTagged<string>("'", 13, 0, 13)),
|
|
factory.Markup(" foo='").With(SpanChunkGenerator.Null),
|
|
new MarkupBlock(
|
|
factory.Markup("@").With(new LiteralAttributeChunkGenerator(new LocationTagged<string>(string.Empty, 11, 0, 11), new LocationTagged<string>("@", 11, 0, 11))).Accepts(AcceptedCharactersInternal.None),
|
|
factory.Markup("@").With(SpanChunkGenerator.Null).Accepts(AcceptedCharactersInternal.None)),
|
|
factory.Markup("'").With(SpanChunkGenerator.Null)),
|
|
factory.Markup(" />")))
|
|
},
|
|
{
|
|
// Double transition at the end of attribute value
|
|
"<span foo='abc@@' />",
|
|
new MarkupBlock(
|
|
new MarkupTagBlock(
|
|
factory.Markup("<span"),
|
|
new MarkupBlock(
|
|
new AttributeBlockChunkGenerator("foo", new LocationTagged<string>(" foo='", 5, 0, 5), new LocationTagged<string>("'", 16, 0, 16)),
|
|
factory.Markup(" foo='").With(SpanChunkGenerator.Null),
|
|
factory.Markup("abc").With(new LiteralAttributeChunkGenerator(new LocationTagged<string>(string.Empty, 11, 0, 11), new LocationTagged<string>("abc", 11, 0, 11))),
|
|
new MarkupBlock(
|
|
factory.Markup("@").With(new LiteralAttributeChunkGenerator(new LocationTagged<string>(string.Empty, 14, 0, 14), new LocationTagged<string>("@", 14, 0, 14))).Accepts(AcceptedCharactersInternal.None),
|
|
factory.Markup("@").With(SpanChunkGenerator.Null).Accepts(AcceptedCharactersInternal.None)),
|
|
factory.Markup("'").With(SpanChunkGenerator.Null)),
|
|
factory.Markup(" />")))
|
|
},
|
|
{
|
|
// Double transition at the beginning of attribute value
|
|
"<span foo='@@def' />",
|
|
new MarkupBlock(
|
|
new MarkupTagBlock(
|
|
factory.Markup("<span"),
|
|
new MarkupBlock(
|
|
new AttributeBlockChunkGenerator("foo", new LocationTagged<string>(" foo='", 5, 0, 5), new LocationTagged<string>("'", 16, 0, 16)),
|
|
factory.Markup(" foo='").With(SpanChunkGenerator.Null),
|
|
new MarkupBlock(
|
|
factory.Markup("@").With(new LiteralAttributeChunkGenerator(new LocationTagged<string>(string.Empty, 11, 0, 11), new LocationTagged<string>("@", 11, 0, 11))).Accepts(AcceptedCharactersInternal.None),
|
|
factory.Markup("@").With(SpanChunkGenerator.Null).Accepts(AcceptedCharactersInternal.None)),
|
|
factory.Markup("def").With(new LiteralAttributeChunkGenerator(new LocationTagged<string>(string.Empty, 13, 0, 13), new LocationTagged<string>("def", 13, 0, 13))),
|
|
factory.Markup("'").With(SpanChunkGenerator.Null)),
|
|
factory.Markup(" />")))
|
|
},
|
|
{
|
|
// Double transition in between attribute value
|
|
"<span foo='abc @@ def' />",
|
|
new MarkupBlock(
|
|
new MarkupTagBlock(
|
|
factory.Markup("<span"),
|
|
new MarkupBlock(
|
|
new AttributeBlockChunkGenerator("foo", new LocationTagged<string>(" foo='", 5, 0, 5), new LocationTagged<string>("'", 21, 0, 21)),
|
|
factory.Markup(" foo='").With(SpanChunkGenerator.Null),
|
|
factory.Markup("abc").With(new LiteralAttributeChunkGenerator(new LocationTagged<string>(string.Empty, 11, 0, 11), new LocationTagged<string>("abc", 11, 0, 11))),
|
|
new MarkupBlock(
|
|
factory.Markup(" @").With(new LiteralAttributeChunkGenerator(new LocationTagged<string>(" ", 14, 0, 14), new LocationTagged<string>("@", 15, 0, 15))).Accepts(AcceptedCharactersInternal.None),
|
|
factory.Markup("@").With(SpanChunkGenerator.Null).Accepts(AcceptedCharactersInternal.None)),
|
|
factory.Markup(" def").With(new LiteralAttributeChunkGenerator(new LocationTagged<string>(" ", 17, 0, 17), new LocationTagged<string>("def", 18, 0, 18))),
|
|
factory.Markup("'").With(SpanChunkGenerator.Null)),
|
|
factory.Markup(" />")))
|
|
},
|
|
{
|
|
// Double transition with expression block
|
|
"<span foo='@@@DateTime.Now' />",
|
|
new MarkupBlock(
|
|
new MarkupTagBlock(
|
|
factory.Markup("<span"),
|
|
new MarkupBlock(
|
|
new AttributeBlockChunkGenerator("foo", new LocationTagged<string>(" foo='", 5, 0, 5), new LocationTagged<string>("'", 26, 0, 26)),
|
|
factory.Markup(" foo='").With(SpanChunkGenerator.Null),
|
|
new MarkupBlock(
|
|
factory.Markup("@").With(new LiteralAttributeChunkGenerator(new LocationTagged<string>(string.Empty, 11, 0, 11), new LocationTagged<string>("@", 11, 0, 11))).Accepts(AcceptedCharactersInternal.None),
|
|
factory.Markup("@").With(SpanChunkGenerator.Null).Accepts(AcceptedCharactersInternal.None)),
|
|
new MarkupBlock(
|
|
new DynamicAttributeBlockChunkGenerator(new LocationTagged<string>(string.Empty, 13, 0, 13), 13, 0, 13),
|
|
factory.EmptyHtml().With(SpanChunkGenerator.Null),
|
|
datetimeBlock),
|
|
factory.Markup("'").With(SpanChunkGenerator.Null)),
|
|
factory.Markup(" />")))
|
|
},
|
|
{
|
|
"<span foo='@DateTime.Now @@' />",
|
|
new MarkupBlock(
|
|
new MarkupTagBlock(
|
|
factory.Markup("<span"),
|
|
new MarkupBlock(
|
|
new AttributeBlockChunkGenerator("foo", new LocationTagged<string>(" foo='", 5, 0, 5), new LocationTagged<string>("'", 27, 0, 27)),
|
|
factory.Markup(" foo='").With(SpanChunkGenerator.Null),
|
|
new MarkupBlock(
|
|
new DynamicAttributeBlockChunkGenerator(new LocationTagged<string>(string.Empty, 11, 0, 11), 11, 0, 11),
|
|
datetimeBlock),
|
|
new MarkupBlock(
|
|
factory.Markup(" @").With(new LiteralAttributeChunkGenerator(new LocationTagged<string>(" ", 24, 0, 24), new LocationTagged<string>("@", 25, 0, 25))).Accepts(AcceptedCharactersInternal.None),
|
|
factory.Markup("@").With(SpanChunkGenerator.Null).Accepts(AcceptedCharactersInternal.None)),
|
|
factory.Markup("'").With(SpanChunkGenerator.Null)),
|
|
factory.Markup(" />")))
|
|
},
|
|
{
|
|
"<span foo='@(2+3)@@@DateTime.Now' />",
|
|
new MarkupBlock(
|
|
new MarkupTagBlock(
|
|
factory.Markup("<span"),
|
|
new MarkupBlock(
|
|
new AttributeBlockChunkGenerator("foo", new LocationTagged<string>(" foo='", 5, 0, 5), new LocationTagged<string>("'", 32, 0, 32)),
|
|
factory.Markup(" foo='").With(SpanChunkGenerator.Null),
|
|
new MarkupBlock(
|
|
new DynamicAttributeBlockChunkGenerator(new LocationTagged<string>(string.Empty, 11, 0, 11), 11, 0, 11),
|
|
new ExpressionBlock(
|
|
factory.CodeTransition(),
|
|
factory.MetaCode("(").With(SpanChunkGenerator.Null).Accepts(AcceptedCharactersInternal.None),
|
|
factory.Code("2+3").AsExpression(),
|
|
factory.MetaCode(")").With(SpanChunkGenerator.Null).Accepts(AcceptedCharactersInternal.None))),
|
|
new MarkupBlock(
|
|
factory.Markup("@").With(new LiteralAttributeChunkGenerator(new LocationTagged<string>(string.Empty, 17, 0, 17), new LocationTagged<string>("@", 17, 0, 17))).Accepts(AcceptedCharactersInternal.None),
|
|
factory.Markup("@").With(SpanChunkGenerator.Null).Accepts(AcceptedCharactersInternal.None)),
|
|
new MarkupBlock(
|
|
new DynamicAttributeBlockChunkGenerator(new LocationTagged<string>(string.Empty, 19, 0, 19), 19, 0, 19),
|
|
factory.EmptyHtml().With(SpanChunkGenerator.Null),
|
|
datetimeBlock),
|
|
factory.Markup("'").With(SpanChunkGenerator.Null)),
|
|
factory.Markup(" />")))
|
|
},
|
|
{
|
|
"<span foo='@@@(2+3)' />",
|
|
new MarkupBlock(
|
|
new MarkupTagBlock(
|
|
factory.Markup("<span"),
|
|
new MarkupBlock(
|
|
new AttributeBlockChunkGenerator("foo", new LocationTagged<string>(" foo='", 5, 0, 5), new LocationTagged<string>("'", 19, 0, 19)),
|
|
factory.Markup(" foo='").With(SpanChunkGenerator.Null),
|
|
new MarkupBlock(
|
|
factory.Markup("@").With(new LiteralAttributeChunkGenerator(new LocationTagged<string>(string.Empty, 11, 0, 11), new LocationTagged<string>("@", 11, 0, 11))).Accepts(AcceptedCharactersInternal.None),
|
|
factory.Markup("@").With(SpanChunkGenerator.Null).Accepts(AcceptedCharactersInternal.None)),
|
|
new MarkupBlock(
|
|
new DynamicAttributeBlockChunkGenerator(new LocationTagged<string>(string.Empty, 13, 0, 13), 13, 0, 13),
|
|
factory.EmptyHtml().With(SpanChunkGenerator.Null),
|
|
new ExpressionBlock(
|
|
factory.CodeTransition(),
|
|
factory.MetaCode("(").With(SpanChunkGenerator.Null).Accepts(AcceptedCharactersInternal.None),
|
|
factory.Code("2+3").AsExpression(),
|
|
factory.MetaCode(")").With(SpanChunkGenerator.Null).Accepts(AcceptedCharactersInternal.None))),
|
|
factory.Markup("'").With(SpanChunkGenerator.Null)),
|
|
factory.Markup(" />")))
|
|
},
|
|
{
|
|
"<span foo='@DateTime.Now@@' />",
|
|
new MarkupBlock(
|
|
new MarkupTagBlock(
|
|
factory.Markup("<span"),
|
|
new MarkupBlock(
|
|
new AttributeBlockChunkGenerator("foo", new LocationTagged<string>(" foo='", 5, 0, 5), new LocationTagged<string>("'", 26, 0, 26)),
|
|
factory.Markup(" foo='").With(SpanChunkGenerator.Null),
|
|
new MarkupBlock(
|
|
new DynamicAttributeBlockChunkGenerator(new LocationTagged<string>(string.Empty, 11, 0, 11), 11, 0, 11),
|
|
datetimeBlock),
|
|
new MarkupBlock(
|
|
factory.Markup("@").With(new LiteralAttributeChunkGenerator(new LocationTagged<string>(string.Empty, 24, 0, 24), new LocationTagged<string>("@", 24, 0, 24))).Accepts(AcceptedCharactersInternal.None),
|
|
factory.Markup("@").With(SpanChunkGenerator.Null).Accepts(AcceptedCharactersInternal.None)),
|
|
factory.Markup("'").With(SpanChunkGenerator.Null)),
|
|
factory.Markup(" />")))
|
|
},
|
|
{
|
|
// Double transition with email in attribute value
|
|
"<span foo='abc@def.com @@' />",
|
|
new MarkupBlock(
|
|
new MarkupTagBlock(
|
|
factory.Markup("<span"),
|
|
new MarkupBlock(
|
|
new AttributeBlockChunkGenerator("foo", new LocationTagged<string>(" foo='", 5, 0, 5), new LocationTagged<string>("'", 25, 0, 25)),
|
|
factory.Markup(" foo='").With(SpanChunkGenerator.Null),
|
|
factory.Markup("abc@def.com").With(new LiteralAttributeChunkGenerator(new LocationTagged<string>(string.Empty, 11, 0, 11), new LocationTagged<string>("abc@def.com", 11, 0, 11))),
|
|
new MarkupBlock(
|
|
factory.Markup(" @").With(new LiteralAttributeChunkGenerator(new LocationTagged<string>(" ", 22, 0, 22), new LocationTagged<string>("@", 23, 0, 23))).Accepts(AcceptedCharactersInternal.None),
|
|
factory.Markup("@").With(SpanChunkGenerator.Null).Accepts(AcceptedCharactersInternal.None)),
|
|
factory.Markup("'").With(SpanChunkGenerator.Null)),
|
|
factory.Markup(" />")))
|
|
},
|
|
{
|
|
"<span foo='abc@@def.com @@' />",
|
|
new MarkupBlock(
|
|
new MarkupTagBlock(
|
|
factory.Markup("<span"),
|
|
new MarkupBlock(
|
|
new AttributeBlockChunkGenerator("foo", new LocationTagged<string>(" foo='", 5, 0, 5), new LocationTagged<string>("'", 26, 0, 26)),
|
|
factory.Markup(" foo='").With(SpanChunkGenerator.Null),
|
|
factory.Markup("abc").With(new LiteralAttributeChunkGenerator(new LocationTagged<string>(string.Empty, 11, 0, 11), new LocationTagged<string>("abc", 11, 0, 11))),
|
|
new MarkupBlock(
|
|
factory.Markup("@").With(new LiteralAttributeChunkGenerator(new LocationTagged<string>(string.Empty, 14, 0, 14), new LocationTagged<string>("@", 14, 0, 14))).Accepts(AcceptedCharactersInternal.None),
|
|
factory.Markup("@").With(SpanChunkGenerator.Null).Accepts(AcceptedCharactersInternal.None)),
|
|
factory.Markup("def.com").With(new LiteralAttributeChunkGenerator(new LocationTagged<string>(string.Empty, 16, 0, 16), new LocationTagged<string>("def.com", 16, 0, 16))),
|
|
new MarkupBlock(
|
|
factory.Markup(" @").With(new LiteralAttributeChunkGenerator(new LocationTagged<string>(" ", 23, 0, 23), new LocationTagged<string>("@", 24, 0, 24))).Accepts(AcceptedCharactersInternal.None),
|
|
factory.Markup("@").With(SpanChunkGenerator.Null).Accepts(AcceptedCharactersInternal.None)),
|
|
factory.Markup("'").With(SpanChunkGenerator.Null)),
|
|
factory.Markup(" />")))
|
|
},
|
|
{
|
|
// Double transition before end of file
|
|
"<span foo='@@",
|
|
new MarkupBlock(
|
|
new MarkupTagBlock(
|
|
factory.Markup("<span"),
|
|
new MarkupBlock(
|
|
new AttributeBlockChunkGenerator("foo", new LocationTagged<string>(" foo='", 5, 0, 5), new LocationTagged<string>(string.Empty, 13, 0, 13)),
|
|
factory.Markup(" foo='").With(SpanChunkGenerator.Null),
|
|
new MarkupBlock(
|
|
factory.Markup("@").With(new LiteralAttributeChunkGenerator(new LocationTagged<string>(string.Empty, 11, 0, 11), new LocationTagged<string>("@", 11, 0, 11))).Accepts(AcceptedCharactersInternal.None),
|
|
factory.Markup("@").With(SpanChunkGenerator.Null).Accepts(AcceptedCharactersInternal.None)))),
|
|
factory.EmptyHtml())
|
|
},
|
|
{
|
|
// Double transition in complex regex in attribute value
|
|
@"<span foo=""/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@@[a-z0-9]([a-z0-9-]*[a-z0-9])?\.([a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i"" />",
|
|
new MarkupBlock(
|
|
new MarkupTagBlock(
|
|
factory.Markup("<span"),
|
|
new MarkupBlock(
|
|
new AttributeBlockChunkGenerator("foo", new LocationTagged<string>(" foo=\"", 5, 0, 5), new LocationTagged<string>("\"", 111, 0, 111)),
|
|
factory.Markup(" foo=\"").With(SpanChunkGenerator.Null),
|
|
factory.Markup(@"/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+").With(new LiteralAttributeChunkGenerator(new LocationTagged<string>(string.Empty, 11, 0, 11), new LocationTagged<string>(@"/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+", 11, 0, 11))),
|
|
new MarkupBlock(
|
|
factory.Markup("@").With(new LiteralAttributeChunkGenerator(new LocationTagged<string>(string.Empty, 43, 0, 43), new LocationTagged<string>("@", 43, 0, 43))).Accepts(AcceptedCharactersInternal.None),
|
|
factory.Markup("@").With(SpanChunkGenerator.Null).Accepts(AcceptedCharactersInternal.None)),
|
|
factory.Markup(@"[a-z0-9]([a-z0-9-]*[a-z0-9])?\.([a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i").With(new LiteralAttributeChunkGenerator(new LocationTagged<string>(string.Empty, 45, 0, 45), new LocationTagged<string>(@"[a-z0-9]([a-z0-9-]*[a-z0-9])?\.([a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i", 45, 0, 45))),
|
|
factory.Markup("\"").With(SpanChunkGenerator.Null)),
|
|
factory.Markup(" />")))
|
|
},
|
|
};
|
|
}
|
|
}
|
|
|
|
[Theory]
|
|
[MemberData(nameof(BlockWithEscapedTransitionData))]
|
|
public void ParseBlock_WithDoubleTransition_DoesNotThrow(string input, object expected)
|
|
{
|
|
FixupSpans = true;
|
|
|
|
// Act & Assert
|
|
ParseDocumentTest(input, (Block)expected);
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseDocument_WithUnexpectedTransitionsInAttributeValue_Throws()
|
|
{
|
|
// Arrange
|
|
var expected = new MarkupBlock(
|
|
new MarkupTagBlock(
|
|
Factory.Markup("<span"),
|
|
new MarkupBlock(
|
|
new AttributeBlockChunkGenerator("foo", new LocationTagged<string>(" foo='", 5, 0, 5), new LocationTagged<string>("'", 14, 0, 14)),
|
|
Factory.Markup(" foo='").With(SpanChunkGenerator.Null),
|
|
new MarkupBlock(
|
|
new DynamicAttributeBlockChunkGenerator(new LocationTagged<string>(string.Empty, 11, 0, 11), 11, 0, 11),
|
|
new ExpressionBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.EmptyCSharp().AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharactersInternal.NonWhiteSpace))),
|
|
new MarkupBlock(
|
|
new DynamicAttributeBlockChunkGenerator(new LocationTagged<string>(" ", 12, 0, 12), 12, 0, 12),
|
|
Factory.Markup(" ").With(SpanChunkGenerator.Null),
|
|
new ExpressionBlock(
|
|
Factory.CodeTransition().Accepts(AcceptedCharactersInternal.None).With(SpanChunkGenerator.Null),
|
|
Factory.EmptyCSharp().AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharactersInternal.NonWhiteSpace))),
|
|
Factory.Markup("'").With(SpanChunkGenerator.Null)),
|
|
Factory.Markup(" />")));
|
|
var expectedErrors = new RazorDiagnostic[]
|
|
{
|
|
RazorDiagnosticFactory.CreateParsing_UnexpectedWhiteSpaceAtStartOfCodeBlock(
|
|
new SourceSpan(new SourceLocation(12, 0, 12), contentLength: 1)),
|
|
RazorDiagnosticFactory.CreateParsing_UnexpectedCharacterAtStartOfCodeBlock(
|
|
new SourceSpan(new SourceLocation(14, 0, 14), contentLength: 4),
|
|
"' />"),
|
|
};
|
|
|
|
// Act & Assert
|
|
ParseDocumentTest("<span foo='@ @' />", expected, expectedErrors);
|
|
}
|
|
}
|
|
}
|