PageRenderTime 51ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

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

https://bitbucket.org/mdavid/aspnetwebstack
C# | 302 lines | 281 code | 16 blank | 5 comment | 0 complexity | 2ffd6a0de2e4f0516aaad9e6fa433c07 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.Test.Framework;
  6. using System.Web.Razor.Text;
  7. using Xunit;
  8. namespace System.Web.Razor.Test.Parser.Html
  9. {
  10. public class HtmlAttributeTest : CsHtmlMarkupParserTestBase
  11. {
  12. [Fact]
  13. public void SimpleLiteralAttribute()
  14. {
  15. ParseBlockTest("<a href='Foo' />",
  16. new MarkupBlock(
  17. Factory.Markup("<a"),
  18. new MarkupBlock(new AttributeBlockCodeGenerator(name: "href", prefix: new LocationTagged<string>(" href='", 2, 0, 2), suffix: new LocationTagged<string>("'", 12, 0, 12)),
  19. Factory.Markup(" href='").With(SpanCodeGenerator.Null),
  20. Factory.Markup("Foo").With(new LiteralAttributeCodeGenerator(prefix: new LocationTagged<string>(String.Empty, 9, 0, 9), value: new LocationTagged<string>("Foo", 9, 0, 9))),
  21. Factory.Markup("'").With(SpanCodeGenerator.Null)),
  22. Factory.Markup(" />").Accepts(AcceptedCharacters.None)));
  23. }
  24. [Fact]
  25. public void MultiPartLiteralAttribute()
  26. {
  27. ParseBlockTest("<a href='Foo Bar Baz' />",
  28. new MarkupBlock(
  29. Factory.Markup("<a"),
  30. new MarkupBlock(new AttributeBlockCodeGenerator(name: "href", prefix: new LocationTagged<string>(" href='", 2, 0, 2), suffix: new LocationTagged<string>("'", 20, 0, 20)),
  31. Factory.Markup(" href='").With(SpanCodeGenerator.Null),
  32. Factory.Markup("Foo").With(new LiteralAttributeCodeGenerator(prefix: new LocationTagged<string>(String.Empty, 9, 0, 9), value: new LocationTagged<string>("Foo", 9, 0, 9))),
  33. Factory.Markup(" Bar").With(new LiteralAttributeCodeGenerator(prefix: new LocationTagged<string>(" ", 12, 0, 12), value: new LocationTagged<string>("Bar", 13, 0, 13))),
  34. Factory.Markup(" Baz").With(new LiteralAttributeCodeGenerator(prefix: new LocationTagged<string>(" ", 16, 0, 16), value: new LocationTagged<string>("Baz", 17, 0, 17))),
  35. Factory.Markup("'").With(SpanCodeGenerator.Null)),
  36. Factory.Markup(" />").Accepts(AcceptedCharacters.None)));
  37. }
  38. [Fact]
  39. public void DoubleQuotedLiteralAttribute()
  40. {
  41. ParseBlockTest("<a href=\"Foo Bar Baz\" />",
  42. new MarkupBlock(
  43. Factory.Markup("<a"),
  44. new MarkupBlock(new AttributeBlockCodeGenerator(name: "href", prefix: new LocationTagged<string>(" href=\"", 2, 0, 2), suffix: new LocationTagged<string>("\"", 20, 0, 20)),
  45. Factory.Markup(" href=\"").With(SpanCodeGenerator.Null),
  46. Factory.Markup("Foo").With(new LiteralAttributeCodeGenerator(prefix: new LocationTagged<string>(String.Empty, 9, 0, 9), value: new LocationTagged<string>("Foo", 9, 0, 9))),
  47. Factory.Markup(" Bar").With(new LiteralAttributeCodeGenerator(prefix: new LocationTagged<string>(" ", 12, 0, 12), value: new LocationTagged<string>("Bar", 13, 0, 13))),
  48. Factory.Markup(" Baz").With(new LiteralAttributeCodeGenerator(prefix: new LocationTagged<string>(" ", 16, 0, 16), value: new LocationTagged<string>("Baz", 17, 0, 17))),
  49. Factory.Markup("\"").With(SpanCodeGenerator.Null)),
  50. Factory.Markup(" />").Accepts(AcceptedCharacters.None)));
  51. }
  52. [Fact]
  53. public void UnquotedLiteralAttribute()
  54. {
  55. ParseBlockTest("<a href=Foo Bar Baz />",
  56. new MarkupBlock(
  57. Factory.Markup("<a"),
  58. new MarkupBlock(new AttributeBlockCodeGenerator(name: "href", prefix: new LocationTagged<string>(" href=", 2, 0, 2), suffix: new LocationTagged<string>(String.Empty, 11, 0, 11)),
  59. Factory.Markup(" href=").With(SpanCodeGenerator.Null),
  60. Factory.Markup("Foo").With(new LiteralAttributeCodeGenerator(prefix: new LocationTagged<string>(String.Empty, 8, 0, 8), value: new LocationTagged<string>("Foo", 8, 0, 8)))),
  61. Factory.Markup(" Bar Baz />").Accepts(AcceptedCharacters.None)));
  62. }
  63. [Fact]
  64. public void SimpleExpressionAttribute()
  65. {
  66. ParseBlockTest("<a href='@foo' />",
  67. new MarkupBlock(
  68. Factory.Markup("<a"),
  69. new MarkupBlock(new AttributeBlockCodeGenerator(name: "href", prefix: new LocationTagged<string>(" href='", 2, 0, 2), suffix: new LocationTagged<string>("'", 13, 0, 13)),
  70. Factory.Markup(" href='").With(SpanCodeGenerator.Null),
  71. new MarkupBlock(new DynamicAttributeBlockCodeGenerator(new LocationTagged<string>(String.Empty, 9, 0, 9), 9, 0, 9),
  72. new ExpressionBlock(
  73. Factory.CodeTransition(),
  74. Factory.Code("foo")
  75. .AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
  76. .Accepts(AcceptedCharacters.NonWhiteSpace))),
  77. Factory.Markup("'").With(SpanCodeGenerator.Null)),
  78. Factory.Markup(" />").Accepts(AcceptedCharacters.None)));
  79. }
  80. [Fact]
  81. public void MultiValueExpressionAttribute()
  82. {
  83. ParseBlockTest("<a href='@foo bar @baz' />",
  84. new MarkupBlock(
  85. Factory.Markup("<a"),
  86. new MarkupBlock(new AttributeBlockCodeGenerator(name: "href", prefix: new LocationTagged<string>(" href='", 2, 0, 2), suffix: new LocationTagged<string>("'", 22, 0, 22)),
  87. Factory.Markup(" href='").With(SpanCodeGenerator.Null),
  88. new MarkupBlock(new DynamicAttributeBlockCodeGenerator(new LocationTagged<string>(String.Empty, 9, 0, 9), 9, 0, 9),
  89. new ExpressionBlock(
  90. Factory.CodeTransition(),
  91. Factory.Code("foo")
  92. .AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
  93. .Accepts(AcceptedCharacters.NonWhiteSpace))),
  94. Factory.Markup(" bar").With(new LiteralAttributeCodeGenerator(new LocationTagged<string>(" ", 13, 0, 13), new LocationTagged<string>("bar", 14, 0, 14))),
  95. new MarkupBlock(new DynamicAttributeBlockCodeGenerator(new LocationTagged<string>(" ", 17, 0, 17), 18, 0, 18),
  96. Factory.Markup(" ").With(SpanCodeGenerator.Null),
  97. new ExpressionBlock(
  98. Factory.CodeTransition(),
  99. Factory.Code("baz")
  100. .AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
  101. .Accepts(AcceptedCharacters.NonWhiteSpace))),
  102. Factory.Markup("'").With(SpanCodeGenerator.Null)),
  103. Factory.Markup(" />").Accepts(AcceptedCharacters.None)));
  104. }
  105. [Fact]
  106. public void VirtualPathAttributesWorkWithConditionalAttributes()
  107. {
  108. ParseBlockTest("<a href='@foo ~/Foo/Bar' />",
  109. new MarkupBlock(
  110. Factory.Markup("<a"),
  111. new MarkupBlock(new AttributeBlockCodeGenerator(name: "href", prefix: new LocationTagged<string>(" href='", 2, 0, 2), suffix: new LocationTagged<string>("'", 23, 0, 23)),
  112. Factory.Markup(" href='").With(SpanCodeGenerator.Null),
  113. new MarkupBlock(new DynamicAttributeBlockCodeGenerator(new LocationTagged<string>(String.Empty, 9, 0, 9), 9, 0, 9),
  114. new ExpressionBlock(
  115. Factory.CodeTransition(),
  116. Factory.Code("foo")
  117. .AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
  118. .Accepts(AcceptedCharacters.NonWhiteSpace))),
  119. Factory.Markup(" ~/Foo/Bar")
  120. .WithEditorHints(EditorHints.VirtualPath)
  121. .With(new LiteralAttributeCodeGenerator(
  122. new LocationTagged<string>(" ", 13, 0, 13),
  123. new LocationTagged<SpanCodeGenerator>(new ResolveUrlCodeGenerator(), 14, 0, 14))),
  124. Factory.Markup("'").With(SpanCodeGenerator.Null)),
  125. Factory.Markup(" />").Accepts(AcceptedCharacters.None)));
  126. }
  127. [Fact]
  128. public void UnquotedAttributeWithCodeWithSpacesInBlock()
  129. {
  130. ParseBlockTest("<input value=@foo />",
  131. new MarkupBlock(
  132. Factory.Markup("<input"),
  133. new MarkupBlock(new AttributeBlockCodeGenerator(name: "value", prefix: new LocationTagged<string>(" value=", 6, 0, 6), suffix: new LocationTagged<string>(String.Empty, 17, 0, 17)),
  134. Factory.Markup(" value=").With(SpanCodeGenerator.Null),
  135. new MarkupBlock(new DynamicAttributeBlockCodeGenerator(new LocationTagged<string>(String.Empty, 13, 0, 13), 13, 0, 13),
  136. new ExpressionBlock(
  137. Factory.CodeTransition(),
  138. Factory.Code("foo")
  139. .AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
  140. .Accepts(AcceptedCharacters.NonWhiteSpace)))),
  141. Factory.Markup(" />").Accepts(AcceptedCharacters.None)));
  142. }
  143. [Fact]
  144. public void UnquotedAttributeWithCodeWithSpacesInDocument()
  145. {
  146. ParseDocumentTest("<input value=@foo />",
  147. new MarkupBlock(
  148. Factory.Markup("<input"),
  149. new MarkupBlock(new AttributeBlockCodeGenerator(name: "value", prefix: new LocationTagged<string>(" value=", 6, 0, 6), suffix: new LocationTagged<string>(String.Empty, 17, 0, 17)),
  150. Factory.Markup(" value=").With(SpanCodeGenerator.Null),
  151. new MarkupBlock(new DynamicAttributeBlockCodeGenerator(new LocationTagged<string>(String.Empty, 13, 0, 13), 13, 0, 13),
  152. new ExpressionBlock(
  153. Factory.CodeTransition(),
  154. Factory.Code("foo")
  155. .AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
  156. .Accepts(AcceptedCharacters.NonWhiteSpace)))),
  157. Factory.Markup(" />")));
  158. }
  159. [Fact]
  160. public void ConditionalAttributeCollapserDoesNotRemoveUrlAttributeValues()
  161. {
  162. // Act
  163. ParserResults results = ParseDocument("<a href='~/Foo/Bar' />");
  164. Block rewritten = new ConditionalAttributeCollapser(new HtmlMarkupParser().BuildSpan).Rewrite(results.Document);
  165. rewritten = new MarkupCollapser(new HtmlMarkupParser().BuildSpan).Rewrite(rewritten);
  166. // Assert
  167. Assert.Equal(0, results.ParserErrors.Count);
  168. EvaluateParseTree(rewritten,
  169. new MarkupBlock(
  170. Factory.Markup("<a"),
  171. new MarkupBlock(new AttributeBlockCodeGenerator(name: "href", prefix: new LocationTagged<string>(" href='", 2, 0, 2), suffix: new LocationTagged<string>("'", 18, 0, 18)),
  172. Factory.Markup(" href='").With(SpanCodeGenerator.Null),
  173. Factory.Markup("~/Foo/Bar")
  174. .WithEditorHints(EditorHints.VirtualPath)
  175. .With(new LiteralAttributeCodeGenerator(
  176. new LocationTagged<string>(String.Empty, 9, 0, 9),
  177. new LocationTagged<SpanCodeGenerator>(new ResolveUrlCodeGenerator(), 9, 0, 9))),
  178. Factory.Markup("'").With(SpanCodeGenerator.Null)),
  179. Factory.Markup(" />")));
  180. }
  181. [Fact]
  182. public void ConditionalAttributesDoNotCreateExtraDataForEntirelyLiteralAttribute()
  183. {
  184. // Arrange
  185. const string code =
  186. #region Big Block o' code
  187. @"<div class=""sidebar"">
  188. <h1>Title</h1>
  189. <p>
  190. As the author, you can <a href=""/Photo/Edit/photoId"">edit</a>
  191. or <a href=""/Photo/Remove/photoId"">remove</a> this photo.
  192. </p>
  193. <dl>
  194. <dt class=""description"">Description</dt>
  195. <dd class=""description"">
  196. The uploader did not provide a description for this photo.
  197. </dd>
  198. <dt class=""uploaded-by"">Uploaded by</dt>
  199. <dd class=""uploaded-by""><a href=""/User/View/user.UserId"">user.DisplayName</a></dd>
  200. <dt class=""upload-date"">Upload date</dt>
  201. <dd class=""upload-date"">photo.UploadDate</dd>
  202. <dt class=""part-of-gallery"">Gallery</dt>
  203. <dd><a href=""/View/gallery.Id"" title=""View gallery.Name gallery"">gallery.Name</a></dd>
  204. <dt class=""tags"">Tags</dt>
  205. <dd class=""tags"">
  206. <ul class=""tags"">
  207. <li>This photo has no tags.</li>
  208. </ul>
  209. <a href=""/Photo/EditTags/photoId"">edit tags</a>
  210. </dd>
  211. </dl>
  212. <p>
  213. <a class=""download"" href=""/Photo/Full/photoId"" title=""Download: (photo.FileTitle + photo.FileExtension)"">Download full photo</a> ((photo.FileSize / 1024) KB)
  214. </p>
  215. </div>
  216. <div class=""main"">
  217. <img class=""large-photo"" alt=""photo.FileTitle"" src=""/Photo/Thumbnail"" />
  218. <h2>Nobody has commented on this photo</h2>
  219. <ol class=""comments"">
  220. <li>
  221. <h3 class=""comment-header"">
  222. <a href=""/User/View/comment.UserId"" title=""View comment.DisplayName's profile"">comment.DisplayName</a> commented at comment.CommentDate:
  223. </h3>
  224. <p class=""comment-body"">comment.CommentText</p>
  225. </li>
  226. </ol>
  227. <form method=""post"" action="""">
  228. <fieldset id=""addComment"">
  229. <legend>Post new comment</legend>
  230. <ol>
  231. <li>
  232. <label for=""newComment"">Comment</label>
  233. <textarea id=""newComment"" name=""newComment"" title=""Your comment"" rows=""6"" cols=""70""></textarea>
  234. </li>
  235. </ol>
  236. <p class=""form-actions"">
  237. <input type=""submit"" title=""Add comment"" value=""Add comment"" />
  238. </p>
  239. </fieldset>
  240. </form>
  241. </div>";
  242. #endregion
  243. // Act
  244. ParserResults results = ParseDocument(code);
  245. Block rewritten = new ConditionalAttributeCollapser(new HtmlMarkupParser().BuildSpan).Rewrite(results.Document);
  246. rewritten = new MarkupCollapser(new HtmlMarkupParser().BuildSpan).Rewrite(rewritten);
  247. // Assert
  248. Assert.Equal(0, results.ParserErrors.Count);
  249. EvaluateParseTree(rewritten, new MarkupBlock(Factory.Markup(code)));
  250. }
  251. [Fact]
  252. public void ConditionalAttributesAreDisabledForDataAttributesInBlock()
  253. {
  254. ParseBlockTest("<span data-foo='@foo'></span>",
  255. new MarkupBlock(
  256. Factory.Markup("<span"),
  257. new MarkupBlock(
  258. Factory.Markup(" data-foo='"),
  259. new ExpressionBlock(
  260. Factory.CodeTransition(),
  261. Factory.Code("foo")
  262. .AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
  263. .Accepts(AcceptedCharacters.NonWhiteSpace)),
  264. Factory.Markup("'")),
  265. Factory.Markup("></span>").Accepts(AcceptedCharacters.None)));
  266. }
  267. [Fact]
  268. public void ConditionalAttributesAreDisabledForDataAttributesInDocument()
  269. {
  270. ParseDocumentTest("<span data-foo='@foo'></span>",
  271. new MarkupBlock(
  272. Factory.Markup("<span"),
  273. new MarkupBlock(
  274. Factory.Markup(" data-foo='"),
  275. new ExpressionBlock(
  276. Factory.CodeTransition(),
  277. Factory.Code("foo")
  278. .AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
  279. .Accepts(AcceptedCharacters.NonWhiteSpace)),
  280. Factory.Markup("'")),
  281. Factory.Markup("></span>")));
  282. }
  283. }
  284. }