PageRenderTime 28ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/test/System.Web.Razor.Test/Parser/Html/HtmlBlockTest.cs

https://bitbucket.org/mdavid/aspnetwebstack
C# | 403 lines | 358 code | 43 blank | 2 comment | 0 complexity | 827d89777f9c742441e1b18416787a9b MD5 | raw file
  1. using System.Web.Razor.Editor;
  2. using System.Web.Razor.Generator;
  3. using System.Web.Razor.Parser;
  4. using System.Web.Razor.Parser.SyntaxTree;
  5. using System.Web.Razor.Resources;
  6. using System.Web.Razor.Test.Framework;
  7. using System.Web.Razor.Text;
  8. using System.Web.Razor.Tokenizer.Symbols;
  9. using Xunit;
  10. using Assert = Microsoft.TestCommon.AssertEx;
  11. namespace System.Web.Razor.Test.Parser.Html
  12. {
  13. public class HtmlBlockTest : CsHtmlMarkupParserTestBase
  14. {
  15. [Fact]
  16. public void ParseBlockMethodThrowsArgNullExceptionOnNullContext()
  17. {
  18. // Arrange
  19. HtmlMarkupParser parser = new HtmlMarkupParser();
  20. // Act and Assert
  21. Assert.Throws<InvalidOperationException>(() => parser.ParseBlock(), RazorResources.Parser_Context_Not_Set);
  22. }
  23. [Fact]
  24. public void ParseBlockHandlesOpenAngleAtEof()
  25. {
  26. ParseDocumentTest(@"@{
  27. <",
  28. new MarkupBlock(
  29. Factory.EmptyHtml(),
  30. new StatementBlock(
  31. Factory.CodeTransition(),
  32. Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
  33. Factory.Code("\r\n").AsStatement(),
  34. new MarkupBlock(
  35. Factory.Markup("<")))),
  36. new RazorError(
  37. String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, RazorResources.BlockName_Code, "}", "{"),
  38. 1, 0, 1));
  39. }
  40. [Fact]
  41. public void ParseBlockHandlesOpenAngleWithProperTagFollowingIt()
  42. {
  43. ParseDocumentTest(@"@{
  44. <
  45. </html>",
  46. new MarkupBlock(
  47. Factory.EmptyHtml(),
  48. new StatementBlock(
  49. Factory.CodeTransition(),
  50. Factory.MetaCode("{").Accepts(AcceptedCharacters.None),
  51. Factory.Code("\r\n").AsStatement(),
  52. new MarkupBlock(
  53. Factory.Markup("<\r\n")
  54. ),
  55. new MarkupBlock(
  56. Factory.Markup(@"</html>").Accepts(AcceptedCharacters.None)
  57. ),
  58. Factory.EmptyCSharp().AsStatement()
  59. )
  60. ),
  61. designTimeParser: true,
  62. expectedErrors: new[]
  63. {
  64. new RazorError(String.Format(RazorResources.ParseError_UnexpectedEndTag, "html"), 7, 2, 0),
  65. new RazorError(String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, "code", "}", "{"), 1, 0, 1)
  66. });
  67. }
  68. [Fact]
  69. public void TagWithoutCloseAngleDoesNotTerminateBlock()
  70. {
  71. ParseBlockTest(@"<
  72. ",
  73. new MarkupBlock(
  74. Factory.Markup("< \r\n ")),
  75. designTimeParser: true,
  76. expectedErrors: new RazorError(String.Format(RazorResources.ParseError_UnfinishedTag, String.Empty), 0, 0, 0));
  77. }
  78. [Fact]
  79. public void ParseBlockAllowsStartAndEndTagsToDifferInCase()
  80. {
  81. SingleSpanBlockTest("<li><p>Foo</P></lI>", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None);
  82. }
  83. [Fact]
  84. public void ParseBlockReadsToEndOfLineIfFirstCharacterAfterTransitionIsColon()
  85. {
  86. ParseBlockTest(@"@:<li>Foo Bar Baz
  87. bork",
  88. new MarkupBlock(
  89. Factory.MarkupTransition(),
  90. Factory.MetaMarkup(":", HtmlSymbolType.Colon),
  91. Factory.Markup("<li>Foo Bar Baz\r\n")
  92. .With(new SingleLineMarkupEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString, AcceptedCharacters.None))
  93. ));
  94. }
  95. [Fact]
  96. public void ParseBlockStopsParsingSingleLineBlockAtEOFIfNoEOLReached()
  97. {
  98. ParseBlockTest("@:foo bar",
  99. new MarkupBlock(
  100. Factory.MarkupTransition(),
  101. Factory.MetaMarkup(":", HtmlSymbolType.Colon),
  102. Factory.Markup(@"foo bar")
  103. .With(new SingleLineMarkupEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString))
  104. ));
  105. }
  106. [Fact]
  107. public void ParseBlockTreatsTwoAtSignsAsEscapeSequence()
  108. {
  109. HtmlParserTestUtils.RunSingleAtEscapeTest(ParseBlockTest);
  110. }
  111. [Fact]
  112. public void ParseBlockTreatsPairsOfAtSignsAsEscapeSequence()
  113. {
  114. HtmlParserTestUtils.RunMultiAtEscapeTest(ParseBlockTest);
  115. }
  116. [Fact]
  117. public void ParseBlockStopsAtMatchingCloseTagToStartTag()
  118. {
  119. SingleSpanBlockTest("<a><b></b></a><c></c>", "<a><b></b></a>", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None);
  120. }
  121. [Fact]
  122. public void ParseBlockParsesUntilMatchingEndTagIfFirstNonWhitespaceCharacterIsStartTag()
  123. {
  124. SingleSpanBlockTest("<baz><boz><biz></biz></boz></baz>", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None);
  125. }
  126. [Fact]
  127. public void ParseBlockAllowsUnclosedTagsAsLongAsItCanRecoverToAnExpectedEndTag()
  128. {
  129. SingleSpanBlockTest("<foo><bar><baz></foo>", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None);
  130. }
  131. [Fact]
  132. public void ParseBlockWithSelfClosingTagJustEmitsTag()
  133. {
  134. SingleSpanBlockTest("<foo />", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None);
  135. }
  136. [Fact]
  137. public void ParseBlockCanHandleSelfClosingTagsWithinBlock()
  138. {
  139. SingleSpanBlockTest("<foo><bar /></foo>", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None);
  140. }
  141. [Fact]
  142. public void ParseBlockSupportsTagsWithAttributes()
  143. {
  144. ParseBlockTest("<foo bar=\"baz\"><biz><boz zoop=zork/></biz></foo>",
  145. new MarkupBlock(
  146. Factory.Markup("<foo"),
  147. new MarkupBlock(new AttributeBlockCodeGenerator("bar", new LocationTagged<string>(" bar=\"", 4, 0, 4), new LocationTagged<string>("\"", 13, 0, 13)),
  148. Factory.Markup(" bar=\"").With(SpanCodeGenerator.Null),
  149. Factory.Markup("baz").With(new LiteralAttributeCodeGenerator(new LocationTagged<string>(String.Empty, 10, 0, 10), new LocationTagged<string>("baz", 10, 0, 10))),
  150. Factory.Markup("\"").With(SpanCodeGenerator.Null)),
  151. Factory.Markup("><biz><boz"),
  152. new MarkupBlock(new AttributeBlockCodeGenerator("zoop", new LocationTagged<string>(" zoop=", 24, 0, 24), new LocationTagged<string>(String.Empty, 34, 0, 34)),
  153. Factory.Markup(" zoop=").With(SpanCodeGenerator.Null),
  154. Factory.Markup("zork").With(new LiteralAttributeCodeGenerator(new LocationTagged<string>(String.Empty, 30, 0, 30), new LocationTagged<string>("zork", 30, 0, 30)))),
  155. Factory.Markup("/></biz></foo>").Accepts(AcceptedCharacters.None)));
  156. }
  157. [Fact]
  158. public void ParseBlockAllowsCloseAngleBracketInAttributeValueIfDoubleQuoted()
  159. {
  160. ParseBlockTest("<foo><bar baz=\">\" /></foo>",
  161. new MarkupBlock(
  162. Factory.Markup("<foo><bar"),
  163. new MarkupBlock(new AttributeBlockCodeGenerator("baz", new LocationTagged<string>(" baz=\"", 9, 0, 9), new LocationTagged<string>("\"", 16, 0, 16)),
  164. Factory.Markup(" baz=\"").With(SpanCodeGenerator.Null),
  165. Factory.Markup(">").With(new LiteralAttributeCodeGenerator(new LocationTagged<string>(String.Empty, 15, 0, 15), new LocationTagged<string>(">", 15, 0, 15))),
  166. Factory.Markup("\"").With(SpanCodeGenerator.Null)),
  167. Factory.Markup(" /></foo>").Accepts(AcceptedCharacters.None)));
  168. }
  169. [Fact]
  170. public void ParseBlockAllowsCloseAngleBracketInAttributeValueIfSingleQuoted()
  171. {
  172. ParseBlockTest("<foo><bar baz=\'>\' /></foo>",
  173. new MarkupBlock(
  174. Factory.Markup("<foo><bar"),
  175. new MarkupBlock(new AttributeBlockCodeGenerator("baz", new LocationTagged<string>(" baz='", 9, 0, 9), new LocationTagged<string>("'", 16, 0, 16)),
  176. Factory.Markup(" baz='").With(SpanCodeGenerator.Null),
  177. Factory.Markup(">").With(new LiteralAttributeCodeGenerator(new LocationTagged<string>(String.Empty, 15, 0, 15), new LocationTagged<string>(">", 15, 0, 15))),
  178. Factory.Markup("'").With(SpanCodeGenerator.Null)),
  179. Factory.Markup(" /></foo>").Accepts(AcceptedCharacters.None)));
  180. }
  181. [Fact]
  182. public void ParseBlockAllowsSlashInAttributeValueIfDoubleQuoted()
  183. {
  184. ParseBlockTest("<foo><bar baz=\"/\"></bar></foo>",
  185. new MarkupBlock(
  186. Factory.Markup("<foo><bar"),
  187. new MarkupBlock(new AttributeBlockCodeGenerator("baz", new LocationTagged<string>(" baz=\"", 9, 0, 9), new LocationTagged<string>("\"", 16, 0, 16)),
  188. Factory.Markup(" baz=\"").With(SpanCodeGenerator.Null),
  189. Factory.Markup("/").With(new LiteralAttributeCodeGenerator(new LocationTagged<string>(String.Empty, 15, 0, 15), new LocationTagged<string>("/", 15, 0, 15))),
  190. Factory.Markup("\"").With(SpanCodeGenerator.Null)),
  191. Factory.Markup("></bar></foo>").Accepts(AcceptedCharacters.None)));
  192. }
  193. [Fact]
  194. public void ParseBlockAllowsSlashInAttributeValueIfSingleQuoted()
  195. {
  196. ParseBlockTest("<foo><bar baz=\'/\'></bar></foo>",
  197. new MarkupBlock(
  198. Factory.Markup("<foo><bar"),
  199. new MarkupBlock(new AttributeBlockCodeGenerator("baz", new LocationTagged<string>(" baz='", 9, 0, 9), new LocationTagged<string>("'", 16, 0, 16)),
  200. Factory.Markup(" baz='").With(SpanCodeGenerator.Null),
  201. Factory.Markup("/").With(new LiteralAttributeCodeGenerator(new LocationTagged<string>(String.Empty, 15, 0, 15), new LocationTagged<string>("/", 15, 0, 15))),
  202. Factory.Markup("'").With(SpanCodeGenerator.Null)),
  203. Factory.Markup("></bar></foo>").Accepts(AcceptedCharacters.None)));
  204. }
  205. [Fact]
  206. public void ParseBlockTerminatesAtEOF()
  207. {
  208. SingleSpanBlockTest("<foo>", "<foo>", BlockType.Markup, SpanKind.Markup,
  209. new RazorError(String.Format(RazorResources.ParseError_MissingEndTag, "foo"), new SourceLocation(0, 0, 0)));
  210. }
  211. [Fact]
  212. public void ParseBlockSupportsCommentAsBlock()
  213. {
  214. SingleSpanBlockTest("<!-- foo -->", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None);
  215. }
  216. [Fact]
  217. public void ParseBlockSupportsCommentWithinBlock()
  218. {
  219. SingleSpanBlockTest("<foo>bar<!-- zoop -->baz</foo>", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None);
  220. }
  221. [Fact]
  222. public void ParseBlockProperlyBalancesCommentStartAndEndTags()
  223. {
  224. SingleSpanBlockTest("<!--<foo></bar>-->", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None);
  225. }
  226. [Fact]
  227. public void ParseBlockTerminatesAtEOFWhenParsingComment()
  228. {
  229. SingleSpanBlockTest("<!--<foo>", "<!--<foo>", BlockType.Markup, SpanKind.Markup);
  230. }
  231. [Fact]
  232. public void ParseBlockOnlyTerminatesCommentOnFullEndSequence()
  233. {
  234. SingleSpanBlockTest("<!--<foo>--</bar>-->", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None);
  235. }
  236. [Fact]
  237. public void ParseBlockTerminatesCommentAtFirstOccurrenceOfEndSequence()
  238. {
  239. SingleSpanBlockTest("<foo><!--<foo></bar-->--></foo>", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None);
  240. }
  241. [Fact]
  242. public void ParseBlockTreatsMalformedTagsAsContent()
  243. {
  244. SingleSpanBlockTest(
  245. "<foo></!-- bar --></foo>",
  246. "<foo></!-- bar -->",
  247. BlockType.Markup,
  248. SpanKind.Markup,
  249. AcceptedCharacters.None,
  250. new RazorError(String.Format(RazorResources.ParseError_MissingEndTag, "foo"), 0, 0, 0));
  251. }
  252. [Fact]
  253. public void ParseBlockParsesSGMLDeclarationAsEmptyTag()
  254. {
  255. SingleSpanBlockTest("<foo><!DOCTYPE foo bar baz></foo>", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None);
  256. }
  257. [Fact]
  258. public void ParseBlockTerminatesSGMLDeclarationAtFirstCloseAngle()
  259. {
  260. SingleSpanBlockTest("<foo><!DOCTYPE foo bar> baz></foo>", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None);
  261. }
  262. [Fact]
  263. public void ParseBlockParsesXMLProcessingInstructionAsEmptyTag()
  264. {
  265. SingleSpanBlockTest("<foo><?xml foo bar baz?></foo>", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None);
  266. }
  267. [Fact]
  268. public void ParseBlockTerminatesXMLProcessingInstructionAtQuestionMarkCloseAnglePair()
  269. {
  270. SingleSpanBlockTest("<foo><?xml foo bar?> baz</foo>", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None);
  271. }
  272. [Fact]
  273. public void ParseBlockDoesNotTerminateXMLProcessingInstructionAtCloseAngleUnlessPreceededByQuestionMark()
  274. {
  275. SingleSpanBlockTest("<foo><?xml foo bar> baz?></foo>", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None);
  276. }
  277. [Fact]
  278. public void ParseBlockSupportsScriptTagsWithLessThanSignsInThem()
  279. {
  280. SingleSpanBlockTest(@"<script>if(foo<bar) { alert(""baz"");)</script>", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None);
  281. }
  282. [Fact]
  283. public void ParseBlockSupportsScriptTagsWithSpacedLessThanSignsInThem()
  284. {
  285. SingleSpanBlockTest(@"<script>if(foo < bar) { alert(""baz"");)</script>", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None);
  286. }
  287. [Fact]
  288. public void ParseBlockAcceptsEmptyTextTag()
  289. {
  290. ParseBlockTest("<text/>",
  291. new MarkupBlock(
  292. Factory.MarkupTransition("<text/>")
  293. ));
  294. }
  295. [Fact]
  296. public void ParseBlockAcceptsTextTagAsOuterTagButDoesNotRender()
  297. {
  298. ParseBlockTest("<text>Foo Bar <foo> Baz</text> zoop",
  299. new MarkupBlock(
  300. Factory.MarkupTransition("<text>"),
  301. Factory.Markup("Foo Bar <foo> Baz"),
  302. Factory.MarkupTransition("</text>"),
  303. Factory.Markup(" ").Accepts(AcceptedCharacters.None)
  304. ));
  305. }
  306. [Fact]
  307. public void ParseBlockRendersLiteralTextTagIfDoubled()
  308. {
  309. ParseBlockTest("<text><text>Foo Bar <foo> Baz</text></text> zoop",
  310. new MarkupBlock(
  311. Factory.MarkupTransition("<text>"),
  312. Factory.Markup("<text>Foo Bar <foo> Baz</text>"),
  313. Factory.MarkupTransition("</text>"),
  314. Factory.Markup(" ").Accepts(AcceptedCharacters.None)
  315. ));
  316. }
  317. [Fact]
  318. public void ParseBlockDoesNotConsiderPsuedoTagWithinMarkupBlock()
  319. {
  320. ParseBlockTest("<foo><text><bar></bar></foo>",
  321. new MarkupBlock(
  322. Factory.Markup("<foo><text><bar></bar></foo>").Accepts(AcceptedCharacters.None)
  323. ));
  324. }
  325. [Fact]
  326. public void ParseBlockStopsParsingMidEmptyTagIfEOFReached()
  327. {
  328. ParseBlockTest("<br/",
  329. new MarkupBlock(
  330. Factory.Markup("<br/")
  331. ),
  332. new RazorError(String.Format(RazorResources.ParseError_UnfinishedTag, "br"), SourceLocation.Zero));
  333. }
  334. [Fact]
  335. public void ParseBlockCorrectlyHandlesSingleLineOfMarkupWithEmbeddedStatement()
  336. {
  337. ParseBlockTest("<div>Foo @if(true) {} Bar</div>",
  338. new MarkupBlock(
  339. Factory.Markup("<div>Foo "),
  340. new StatementBlock(
  341. Factory.CodeTransition(),
  342. Factory.Code("if(true) {}").AsStatement()),
  343. Factory.Markup(" Bar</div>").Accepts(AcceptedCharacters.None)));
  344. }
  345. [Fact]
  346. public void ParseBlockIgnoresTagsInContentsOfScriptTag()
  347. {
  348. ParseBlockTest(@"<script>foo<bar baz='@boz'></script>",
  349. new MarkupBlock(
  350. Factory.Markup("<script>foo<bar baz='"),
  351. new ExpressionBlock(
  352. Factory.CodeTransition(),
  353. Factory.Code("boz")
  354. .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: false)
  355. .Accepts(AcceptedCharacters.NonWhiteSpace)),
  356. Factory.Markup("'></script>")
  357. .Accepts(AcceptedCharacters.None)));
  358. }
  359. }
  360. }