a575963da9
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
353 lines
17 KiB
C#
353 lines
17 KiB
C#
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
|
|
|
|
using System.Web.Razor.Editor;
|
|
using System.Web.Razor.Generator;
|
|
using System.Web.Razor.Parser;
|
|
using System.Web.Razor.Parser.SyntaxTree;
|
|
using System.Web.Razor.Test.Framework;
|
|
using System.Web.Razor.Text;
|
|
using System.Web.Razor.Tokenizer.Symbols;
|
|
using Xunit;
|
|
|
|
namespace System.Web.Razor.Test.Parser.Html
|
|
{
|
|
public class HtmlToCodeSwitchTest : CsHtmlMarkupParserTestBase
|
|
{
|
|
[Fact]
|
|
public void ParseBlockSwitchesWhenCharacterBeforeSwapIsNonAlphanumeric()
|
|
{
|
|
ParseBlockTest("<p>foo#@i</p>",
|
|
new MarkupBlock(
|
|
Factory.Markup("<p>foo#"),
|
|
new ExpressionBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.Code("i").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharacters.NonWhiteSpace)),
|
|
Factory.Markup("</p>").Accepts(AcceptedCharacters.None)));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseBlockSwitchesToCodeWhenSwapCharacterEncounteredMidTag()
|
|
{
|
|
ParseBlockTest("<foo @bar />",
|
|
new MarkupBlock(
|
|
Factory.Markup("<foo "),
|
|
new ExpressionBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.Code("bar")
|
|
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
|
|
.Accepts(AcceptedCharacters.NonWhiteSpace)),
|
|
Factory.Markup(" />").Accepts(AcceptedCharacters.None)));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseBlockSwitchesToCodeWhenSwapCharacterEncounteredInAttributeValue()
|
|
{
|
|
ParseBlockTest("<foo bar=\"@baz\" />",
|
|
new MarkupBlock(
|
|
Factory.Markup("<foo"),
|
|
new MarkupBlock(new AttributeBlockCodeGenerator("bar", new LocationTagged<string>(" bar=\"", 4, 0, 4), new LocationTagged<string>("\"", 14, 0, 14)),
|
|
Factory.Markup(" bar=\"").With(SpanCodeGenerator.Null),
|
|
new MarkupBlock(new DynamicAttributeBlockCodeGenerator(new LocationTagged<string>(String.Empty, 10, 0, 10), 10, 0, 10),
|
|
new ExpressionBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.Code("baz")
|
|
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
|
|
.Accepts(AcceptedCharacters.NonWhiteSpace))),
|
|
Factory.Markup("\"").With(SpanCodeGenerator.Null)),
|
|
Factory.Markup(" />").Accepts(AcceptedCharacters.None)));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseBlockSwitchesToCodeWhenSwapCharacterEncounteredInTagContent()
|
|
{
|
|
ParseBlockTest("<foo>@bar<baz>@boz</baz></foo>",
|
|
new MarkupBlock(
|
|
Factory.Markup("<foo>"),
|
|
new ExpressionBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.Code("bar")
|
|
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
|
|
.Accepts(AcceptedCharacters.NonWhiteSpace)),
|
|
Factory.Markup("<baz>"),
|
|
new ExpressionBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.Code("boz")
|
|
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
|
|
.Accepts(AcceptedCharacters.NonWhiteSpace)),
|
|
Factory.Markup("</baz></foo>").Accepts(AcceptedCharacters.None)));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseBlockParsesCodeWithinSingleLineMarkup()
|
|
{
|
|
ParseBlockTest(@"@:<li>Foo @Bar Baz
|
|
bork",
|
|
new MarkupBlock(
|
|
Factory.MarkupTransition(),
|
|
Factory.MetaMarkup(":", HtmlSymbolType.Colon),
|
|
Factory.Markup("<li>Foo ").With(new SingleLineMarkupEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString)),
|
|
new ExpressionBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.Code("Bar")
|
|
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
|
|
.Accepts(AcceptedCharacters.NonWhiteSpace)),
|
|
Factory.Markup(" Baz\r\n")
|
|
.With(new SingleLineMarkupEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString, AcceptedCharacters.None))));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseBlockSupportsCodeWithinComment()
|
|
{
|
|
ParseBlockTest("<foo><!-- @foo --></foo>",
|
|
new MarkupBlock(
|
|
Factory.Markup("<foo><!-- "),
|
|
new ExpressionBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.Code("foo")
|
|
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
|
|
.Accepts(AcceptedCharacters.NonWhiteSpace)),
|
|
Factory.Markup(" --></foo>").Accepts(AcceptedCharacters.None)));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseBlockSupportsCodeWithinSGMLDeclaration()
|
|
{
|
|
ParseBlockTest("<foo><!DOCTYPE foo @bar baz></foo>",
|
|
new MarkupBlock(
|
|
Factory.Markup("<foo><!DOCTYPE foo "),
|
|
new ExpressionBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.Code("bar")
|
|
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
|
|
.Accepts(AcceptedCharacters.NonWhiteSpace)),
|
|
Factory.Markup(" baz></foo>").Accepts(AcceptedCharacters.None)));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseBlockSupportsCodeWithinCDataDeclaration()
|
|
{
|
|
ParseBlockTest("<foo><![CDATA[ foo @bar baz]]></foo>",
|
|
new MarkupBlock(
|
|
Factory.Markup("<foo><![CDATA[ foo "),
|
|
new ExpressionBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.Code("bar")
|
|
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
|
|
.Accepts(AcceptedCharacters.NonWhiteSpace)),
|
|
Factory.Markup(" baz]]></foo>").Accepts(AcceptedCharacters.None)));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseBlockSupportsCodeWithinXMLProcessingInstruction()
|
|
{
|
|
ParseBlockTest("<foo><?xml foo @bar baz?></foo>",
|
|
new MarkupBlock(
|
|
Factory.Markup("<foo><?xml foo "),
|
|
new ExpressionBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.Code("bar")
|
|
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
|
|
.Accepts(AcceptedCharacters.NonWhiteSpace)),
|
|
Factory.Markup(" baz?></foo>").Accepts(AcceptedCharacters.None)));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseBlockDoesNotSwitchToCodeOnEmailAddressInText()
|
|
{
|
|
SingleSpanBlockTest("<foo>anurse@microsoft.com</foo>", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None);
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseBlockDoesNotSwitchToCodeOnEmailAddressInAttribute()
|
|
{
|
|
ParseBlockTest("<a href=\"mailto:anurse@microsoft.com\">Email me</a>",
|
|
new MarkupBlock(
|
|
Factory.Markup("<a"),
|
|
new MarkupBlock(new AttributeBlockCodeGenerator("href", new LocationTagged<string>(" href=\"", 2, 0, 2), new LocationTagged<string>("\"", 36, 0, 36)),
|
|
Factory.Markup(" href=\"").With(SpanCodeGenerator.Null),
|
|
Factory.Markup("mailto:anurse@microsoft.com")
|
|
.With(new LiteralAttributeCodeGenerator(new LocationTagged<string>(String.Empty, 9, 0, 9), new LocationTagged<string>("mailto:anurse@microsoft.com", 9, 0, 9))),
|
|
Factory.Markup("\"").With(SpanCodeGenerator.Null)),
|
|
Factory.Markup(">Email me</a>").Accepts(AcceptedCharacters.None)));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseBlockGivesWhitespacePreceedingAtToCodeIfThereIsNoMarkupOnThatLine()
|
|
{
|
|
ParseBlockTest(@" <ul>
|
|
@foreach(var p in Products) {
|
|
<li>Product: @p.Name</li>
|
|
}
|
|
</ul>",
|
|
new MarkupBlock(
|
|
Factory.Markup(" <ul>\r\n"),
|
|
new StatementBlock(
|
|
Factory.Code(" ").AsStatement(),
|
|
Factory.CodeTransition(),
|
|
Factory.Code("foreach(var p in Products) {\r\n").AsStatement(),
|
|
new MarkupBlock(
|
|
Factory.Markup(" <li>Product: "),
|
|
new ExpressionBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.Code("p.Name")
|
|
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
|
|
.Accepts(AcceptedCharacters.NonWhiteSpace)),
|
|
Factory.Markup("</li>\r\n").Accepts(AcceptedCharacters.None)),
|
|
Factory.Code(" }\r\n").AsStatement().Accepts(AcceptedCharacters.None)),
|
|
Factory.Markup(" </ul>").Accepts(AcceptedCharacters.None)));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseDocumentGivesWhitespacePreceedingAtToCodeIfThereIsNoMarkupOnThatLine()
|
|
{
|
|
ParseDocumentTest(@" <ul>
|
|
@foreach(var p in Products) {
|
|
<li>Product: @p.Name</li>
|
|
}
|
|
</ul>",
|
|
new MarkupBlock(
|
|
Factory.Markup(" <ul>\r\n"),
|
|
new StatementBlock(
|
|
Factory.Code(" ").AsStatement(),
|
|
Factory.CodeTransition(),
|
|
Factory.Code("foreach(var p in Products) {\r\n").AsStatement(),
|
|
new MarkupBlock(
|
|
Factory.Markup(" <li>Product: "),
|
|
new ExpressionBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.Code("p.Name")
|
|
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
|
|
.Accepts(AcceptedCharacters.NonWhiteSpace)),
|
|
Factory.Markup("</li>\r\n").Accepts(AcceptedCharacters.None)),
|
|
Factory.Code(" }\r\n").AsStatement().Accepts(AcceptedCharacters.None)),
|
|
Factory.Markup(" </ul>")));
|
|
}
|
|
|
|
[Fact]
|
|
public void SectionContextGivesWhitespacePreceedingAtToCodeIfThereIsNoMarkupOnThatLine()
|
|
{
|
|
ParseDocumentTest(@"@section foo {
|
|
<ul>
|
|
@foreach(var p in Products) {
|
|
<li>Product: @p.Name</li>
|
|
}
|
|
</ul>
|
|
}",
|
|
new MarkupBlock(
|
|
Factory.EmptyHtml(),
|
|
new SectionBlock(new SectionCodeGenerator("foo"),
|
|
Factory.CodeTransition(),
|
|
Factory.MetaCode("section foo {").AutoCompleteWith(null, atEndOfSpan: true),
|
|
new MarkupBlock(
|
|
Factory.Markup("\r\n <ul>\r\n"),
|
|
new StatementBlock(
|
|
Factory.Code(" ").AsStatement(),
|
|
Factory.CodeTransition(),
|
|
Factory.Code("foreach(var p in Products) {\r\n").AsStatement(),
|
|
new MarkupBlock(
|
|
Factory.Markup(" <li>Product: "),
|
|
new ExpressionBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.Code("p.Name")
|
|
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
|
|
.Accepts(AcceptedCharacters.NonWhiteSpace)),
|
|
Factory.Markup("</li>\r\n").Accepts(AcceptedCharacters.None)),
|
|
Factory.Code(" }\r\n").AsStatement().Accepts(AcceptedCharacters.None)),
|
|
Factory.Markup(" </ul>\r\n")),
|
|
Factory.MetaCode("}").Accepts(AcceptedCharacters.None)),
|
|
Factory.EmptyHtml()));
|
|
}
|
|
|
|
[Fact]
|
|
public void CSharpCodeParserDoesNotAcceptLeadingOrTrailingWhitespaceInDesignMode()
|
|
{
|
|
ParseBlockTest(@" <ul>
|
|
@foreach(var p in Products) {
|
|
<li>Product: @p.Name</li>
|
|
}
|
|
</ul>",
|
|
new MarkupBlock(
|
|
Factory.Markup(" <ul>\r\n "),
|
|
new StatementBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.Code("foreach(var p in Products) {\r\n ").AsStatement(),
|
|
new MarkupBlock(
|
|
Factory.Markup("<li>Product: "),
|
|
new ExpressionBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.Code("p.Name").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharacters.NonWhiteSpace)),
|
|
Factory.Markup("</li>").Accepts(AcceptedCharacters.None)),
|
|
Factory.Code("\r\n }").AsStatement().Accepts(AcceptedCharacters.None)),
|
|
Factory.Markup("\r\n </ul>").Accepts(AcceptedCharacters.None)),
|
|
designTimeParser: true);
|
|
}
|
|
|
|
// Tests for "@@" escape sequence:
|
|
[Fact]
|
|
public void ParseBlockTreatsTwoAtSignsAsEscapeSequence()
|
|
{
|
|
HtmlParserTestUtils.RunSingleAtEscapeTest(ParseBlockTest);
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseBlockTreatsPairsOfAtSignsAsEscapeSequence()
|
|
{
|
|
HtmlParserTestUtils.RunMultiAtEscapeTest(ParseBlockTest);
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseDocumentTreatsTwoAtSignsAsEscapeSequence()
|
|
{
|
|
HtmlParserTestUtils.RunSingleAtEscapeTest(ParseDocumentTest, lastSpanAcceptedCharacters: AcceptedCharacters.Any);
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseDocumentTreatsPairsOfAtSignsAsEscapeSequence()
|
|
{
|
|
HtmlParserTestUtils.RunMultiAtEscapeTest(ParseDocumentTest, lastSpanAcceptedCharacters: AcceptedCharacters.Any);
|
|
}
|
|
|
|
[Fact]
|
|
public void SectionBodyTreatsTwoAtSignsAsEscapeSequence()
|
|
{
|
|
ParseDocumentTest("@section Foo { <foo>@@bar</foo> }",
|
|
new MarkupBlock(
|
|
Factory.EmptyHtml(),
|
|
new SectionBlock(new SectionCodeGenerator("Foo"),
|
|
Factory.CodeTransition(),
|
|
Factory.MetaCode("section Foo {").AutoCompleteWith(null, atEndOfSpan: true),
|
|
new MarkupBlock(
|
|
Factory.Markup(" <foo>"),
|
|
Factory.Markup("@").Hidden(),
|
|
Factory.Markup("@bar</foo> ")),
|
|
Factory.MetaCode("}").Accepts(AcceptedCharacters.None)),
|
|
Factory.EmptyHtml()));
|
|
}
|
|
|
|
[Fact]
|
|
public void SectionBodyTreatsPairsOfAtSignsAsEscapeSequence()
|
|
{
|
|
ParseDocumentTest("@section Foo { <foo>@@@@@bar</foo> }",
|
|
new MarkupBlock(
|
|
Factory.EmptyHtml(),
|
|
new SectionBlock(new SectionCodeGenerator("Foo"),
|
|
Factory.CodeTransition(),
|
|
Factory.MetaCode("section Foo {").AutoCompleteWith(null, atEndOfSpan: true),
|
|
new MarkupBlock(
|
|
Factory.Markup(" <foo>"),
|
|
Factory.Markup("@").Hidden(),
|
|
Factory.Markup("@"),
|
|
Factory.Markup("@").Hidden(),
|
|
Factory.Markup("@"),
|
|
new ExpressionBlock(
|
|
Factory.CodeTransition(),
|
|
Factory.Code("bar")
|
|
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
|
|
.Accepts(AcceptedCharacters.NonWhiteSpace)),
|
|
Factory.Markup("</foo> ")),
|
|
Factory.MetaCode("}").Accepts(AcceptedCharacters.None)),
|
|
Factory.EmptyHtml()));
|
|
}
|
|
}
|
|
}
|