/scalate-core/src/test/scala/org/fusesource/scalate/scaml/ScamlTemplateTest.scala

http://github.com/scalate/scalate · Scala · 1237 lines · 1164 code · 32 blank · 41 comment · 10 complexity · 899a8b3de999966f638cecc9601fb0fd MD5 · raw file

  1. /**
  2. * Copyright (C) 2009-2011 the original author or authors.
  3. * See the notice.md file distributed with this work for additional
  4. * information regarding copyright ownership.
  5. *
  6. * Licensed under the Apache License, Version 2.0 (the "License");
  7. * you may not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. package org.fusesource.scalate.scaml
  19. /**
  20. * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
  21. */
  22. class ScamlTemplateTest extends ScamlTestSupport {
  23. /////////////////////////////////////////////////////////////////////
  24. //
  25. // Filters
  26. //
  27. /////////////////////////////////////////////////////////////////////
  28. testRender(
  29. "You can use `:` to use filters",
  30. """
  31. %html
  32. %p
  33. :plain
  34. Indentation levels are not enforced in filters.
  35. #{Interpolation} is disabled by default
  36. Last line
  37. """, """
  38. <html>
  39. <p>
  40. Indentation levels are not enforced in filters.
  41. #{Interpolation} is disabled by default
  42. Last line
  43. </p>
  44. </html>
  45. """)
  46. testRender(
  47. "Use the `~` filter flag to preserve white space",
  48. """
  49. %html
  50. %p<
  51. :~plain
  52. Indentation levels are not enforced in filters.
  53. #{Interpolation} is disabled by default
  54. Last line
  55. """, """
  56. <html>
  57. <p> Indentation levels are not enforced in filters.&#x000A; #{Interpolation} is disabled by default&#x000A;Last line</p>
  58. </html>
  59. """)
  60. testRender(
  61. "Use the `&` filter flag to enable sanitizing interpolation",
  62. """
  63. %html
  64. %p
  65. :&plain
  66. I like #{ "<strong>" } cheese
  67. """, """
  68. <html>
  69. <p>
  70. I like &lt;strong&gt; cheese
  71. </p>
  72. </html>
  73. """)
  74. testRender(
  75. "Use the `!` filter flag to enable non-sanitizing interpolation",
  76. """
  77. %html
  78. %p
  79. :!plain
  80. I like #{ "<strong>" } cheese #{ "</strong>" }
  81. """, """
  82. <html>
  83. <p>
  84. I like <strong> cheese </strong>
  85. </p>
  86. </html>
  87. """)
  88. testRender(
  89. ":javascript filter can be used to safely insert javascript",
  90. """
  91. %html
  92. %head
  93. :javascript
  94. alert("Hello");
  95. """, """
  96. <html>
  97. <head>
  98. <script type='text/javascript'>
  99. //<![CDATA[
  100. alert("Hello");
  101. //]]>
  102. </script>
  103. </head>
  104. </html>
  105. """)
  106. testRender(
  107. "filters can be chained",
  108. """
  109. %pre
  110. :escaped :javascript
  111. alert("Hello");
  112. """, """
  113. <pre>
  114. &lt;script type='text/javascript'&gt;
  115. //&lt;![CDATA[
  116. alert(&quot;Hello&quot;);
  117. //]]&gt;
  118. &lt;/script&gt;
  119. </pre>
  120. """)
  121. testRender(
  122. "The markdown filter",
  123. """
  124. %p
  125. :markdown
  126. Markdown
  127. =======
  128. Hello, *World*
  129. """, """
  130. <p>
  131. <h1 id = "Markdown">Markdown</h1>
  132. <p>Hello, <em>World</em></p>
  133. </p>
  134. """)
  135. testRender(
  136. "The `&` flag enables sanitized interpolations.",
  137. """
  138. - var flavor = "<raspberry/>"
  139. #content
  140. :&markdown
  141. I *really* prefer #{flavor} jam.
  142. """, """
  143. <div id="content">
  144. <p>I <em>really</em> prefer &lt;raspberry/&gt; jam.</p>
  145. </div>
  146. """)
  147. testRender(
  148. "The `!` flag enables non-sanitized interpolations.",
  149. """
  150. - var flavor = "<raspberry/>"
  151. #content
  152. :!markdown
  153. I *really* prefer #{flavor} jam.
  154. """, """
  155. <div id="content">
  156. <p>I <em>really</em> prefer <raspberry/> jam.</p>
  157. </div>
  158. """)
  159. /////////////////////////////////////////////////////////////////////
  160. //
  161. // Plain Text
  162. //
  163. /////////////////////////////////////////////////////////////////////
  164. testRender(
  165. "Any Scaml line that's not interpreted as something else is taken to be plain text, and passed through unmodified. ",
  166. """
  167. %gee
  168. %whiz
  169. Wow this is cool!
  170. """, """
  171. <gee>
  172. <whiz>
  173. Wow this is cool!
  174. </whiz>
  175. </gee>
  176. """)
  177. testRender(
  178. "HTML tags are passed through unmodified as well.",
  179. """
  180. %p
  181. <div id="blah">Blah!</div>
  182. """, """
  183. <p>
  184. <div id="blah">Blah!</div>
  185. </p>
  186. """)
  187. testRender(
  188. "backslash character escapes the first character of a line, allowing use of otherwise interpreted characters as plain text.",
  189. """
  190. %title
  191. = title
  192. \= title
  193. """, """
  194. <title>
  195. MyPage
  196. = title
  197. </title>
  198. """)
  199. testRender(
  200. "Indenting can be disabled with the ScamlOptions.indent option",
  201. """
  202. %gee
  203. %whiz
  204. Wow this is cool!
  205. """, """
  206. <gee>
  207. <whiz>
  208. Wow this is cool!
  209. </whiz>
  210. </gee>
  211. """, { () =>
  212. ScamlOptions.indent = ""
  213. }, { () =>
  214. ScamlOptions.indent = ScamlOptions.DEFAULT_INDENT
  215. })
  216. testRender(
  217. "Newlines can also be disabled with the ScamlOptions.nl option",
  218. """
  219. %gee
  220. %whiz
  221. Wow this is cool!
  222. """, """
  223. <gee><whiz>Wow this is cool!</whiz></gee>
  224. """, { () =>
  225. ScamlOptions.indent = ""
  226. ScamlOptions.nl = ""
  227. }, { () =>
  228. ScamlOptions.indent = ScamlOptions.DEFAULT_INDENT
  229. ScamlOptions.nl = ScamlOptions.DEFAULT_NL
  230. })
  231. /////////////////////////////////////////////////////////////////////
  232. //
  233. // HTML Elements
  234. //
  235. /////////////////////////////////////////////////////////////////////
  236. testRender(
  237. "a '%tag' can have trailing spaces and nested content",
  238. """
  239. %html
  240. %body
  241. """, """
  242. <html>
  243. <body></body>
  244. </html>
  245. """)
  246. testRender(
  247. "'%tag' renders a start and end tag",
  248. """
  249. %html
  250. """, """
  251. <html></html>
  252. """)
  253. testRender(
  254. "'%tag text' render start tag, text, and end tag on same line",
  255. """
  256. %html test
  257. """, """
  258. <html>test</html>
  259. """)
  260. testRender(
  261. "nested tags are rendered indented",
  262. """
  263. %html
  264. %body
  265. test
  266. """, """
  267. <html>
  268. <body>
  269. test
  270. </body>
  271. </html>
  272. """)
  273. testRender(
  274. "single quoted tags can contain special characters",
  275. """
  276. %'funny.element'.test
  277. hi
  278. """, """
  279. <funny.element class="test">
  280. hi
  281. </funny.element>
  282. """)
  283. testRender(
  284. "'%tag = quality' renders a tag with nested content",
  285. """
  286. %p=quality
  287. %p = quality
  288. """, """
  289. <p>scrumptious</p>
  290. <p>scrumptious</p>
  291. """)
  292. /////////////////////////////////////////////////////////////////////
  293. //
  294. // HTML Elements : Attributes
  295. //
  296. /////////////////////////////////////////////////////////////////////
  297. testRender(
  298. "Brackets represent a Scala hash that is used for specifying the attributes of an element.",
  299. """
  300. %html{:xmlns => "http://www.w3.org/1999/xhtml", "xml:lang" => "en", :lang => "en"}
  301. """, """
  302. <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"></html>
  303. """)
  304. testRender(
  305. "Attribute hashes can also be stretched out over multiple lines to accommodate many attributes.",
  306. """
  307. %script{:type => "text/javascript",
  308. :src => "javascripts/script"}
  309. """, """
  310. <script type="text/javascript" src="javascripts/script"></script>
  311. """)
  312. testRender(
  313. "Scaml also supports a terser, less Scala-specific attribute syntax based on HTML's attributes.",
  314. """
  315. %html(xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en")
  316. """, """
  317. <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"></html>
  318. """)
  319. testRender(
  320. "Scala variables can be used by omitting the quotes.",
  321. """
  322. %a(title=title href=href) Stuff
  323. """, """
  324. <a title="MyPage" href="http://scalate.fusesource.org">Stuff</a>
  325. """)
  326. testRender(
  327. "You can use both syntaxes together.",
  328. """
  329. %a(title="Hello"){:href => "http://scalate.fusesource.org"} Stuff
  330. """, """
  331. <a title="Hello" href="http://scalate.fusesource.org">Stuff</a>
  332. """)
  333. testRender(
  334. "HTML-style attributes can be stretched across multiple lines just like hash-style attributes",
  335. """
  336. %script(type="text/javascript"
  337. src="javascripts/script")
  338. """, """
  339. <script type="text/javascript" src="javascripts/script"></script>
  340. """)
  341. testRender(
  342. "Html Attributes can use complex scala expressions",
  343. """
  344. %div(count={3+4})
  345. """, """
  346. <div count="7"></div>
  347. """)
  348. testRender(
  349. "Html Attributes can use simple scala expressions",
  350. """
  351. %div(count="#{3+4}")
  352. """, """
  353. <div count="7"></div>
  354. """)
  355. testRender(
  356. "Html Attributes can use scala variables",
  357. """
  358. - val count = 5
  359. %div(count=count)
  360. """, """
  361. <div count="5"></div>
  362. """)
  363. testRender(
  364. "Hash Attributes can use simple scala expressions",
  365. """
  366. - val count = 5
  367. %div{:count=>count}
  368. """, """
  369. <div count="5"></div>
  370. """)
  371. testRender(
  372. "Hash Attributes can use complex scala expressions",
  373. """
  374. %div{:count=>{3+4}}
  375. """, """
  376. <div count="7"></div>
  377. """)
  378. testRender(
  379. "Enabled boolean attribute in hash style",
  380. """
  381. %input{:selected => true}
  382. """, """
  383. <input selected="selected"/>
  384. """)
  385. testRender(
  386. "Disabled boolean attribute in hash style",
  387. """
  388. %input{:selected => false}
  389. """, """
  390. <input/>
  391. """)
  392. testRender(
  393. "Enabled boolean attribute in html style",
  394. """
  395. %input(selected=true)
  396. """, """
  397. <input selected="selected"/>
  398. """)
  399. testRender(
  400. "Disabled boolean attribute in html style",
  401. """
  402. %input(selected=false)
  403. """, """
  404. <input/>
  405. """)
  406. testRender(
  407. "Enabled boolean attribute in html style using shorthand",
  408. """
  409. %input(selected)
  410. """, """
  411. <input selected="selected"/>
  412. """)
  413. testRender(
  414. "Mixing attributes with complex expressions",
  415. """
  416. %p{ :counter1=>{1 + 2}, :counter2=>10 }
  417. """, """
  418. <p counter1="3" counter2="10"></p>
  419. """)
  420. /////////////////////////////////////////////////////////////////////
  421. //
  422. // HTML Elements : Class and ID: `.` and `#`
  423. //
  424. /////////////////////////////////////////////////////////////////////
  425. testRender(
  426. "`.` and `#` are used as shortcuts to specify the `class` and `id` attributes of an element",
  427. """
  428. %div#things
  429. %span#rice Chicken Fried
  430. %p.beans{ :food => "true" } The magical fruit
  431. %h1.class.otherclass#id La La La
  432. """, """
  433. <div id="things">
  434. <span id="rice">Chicken Fried</span>
  435. <p class="beans" food="true">The magical fruit</p>
  436. <h1 id="id" class="class otherclass">La La La</h1>
  437. </div>
  438. """)
  439. testRender(
  440. "Another `.` and `#` example",
  441. """
  442. #content
  443. .articles
  444. .article.title Doogie Howser Comes Out
  445. .article.date 2006-11-05
  446. .article.entry
  447. Neil Patrick Harris would like to dispel any rumors that he is straight
  448. """, """
  449. <div id="content">
  450. <div class="articles">
  451. <div class="article title">Doogie Howser Comes Out</div>
  452. <div class="article date">2006-11-05</div>
  453. <div class="article entry">
  454. Neil Patrick Harris would like to dispel any rumors that he is straight
  455. </div>
  456. </div>
  457. </div>
  458. """)
  459. // Edge cases
  460. testRender(
  461. "'%tag#i1#i2' last id specified wins",
  462. """
  463. %html#i1#i2
  464. """, """
  465. <html id="i2"></html>
  466. """)
  467. testRender(
  468. "'%tag.c1' renders a tag with a class",
  469. """
  470. %html.c1
  471. """, """
  472. <html class="c1"></html>
  473. """)
  474. testRender(
  475. "'%tag.c1.c2' renders a tag with multiple classes",
  476. """
  477. %html.c1.c2
  478. """, """
  479. <html class="c1 c2"></html>
  480. """)
  481. testRender(
  482. "Any css class/name can be used.",
  483. """
  484. .my-class
  485. ._whacky_1
  486. """, """
  487. <div class="my-class"></div>
  488. <div class="_whacky_1"></div>
  489. """)
  490. /////////////////////////////////////////////////////////////////////
  491. //
  492. // HTML Elements : Implicit Div Elements
  493. //
  494. /////////////////////////////////////////////////////////////////////
  495. testRender(
  496. "If you only define a class and/or id using `.` or `#`, a div is automatically used.",
  497. """
  498. #collection
  499. .item
  500. .description What a cool item!
  501. """, """
  502. <div id="collection">
  503. <div class="item">
  504. <div class="description">What a cool item!</div>
  505. </div>
  506. </div>
  507. """)
  508. /////////////////////////////////////////////////////////////////////
  509. //
  510. // HTML Elements : Self-Closing Tags : `/`
  511. //
  512. /////////////////////////////////////////////////////////////////////
  513. testRender(
  514. "The forward slash character, when placed at the end of a tag definition, causes the tag to be self-closed.",
  515. """
  516. %br/
  517. %meta{"http-equiv" => "Content-Type", :content => "text/html"}/
  518. """, """
  519. <br/>
  520. <meta http-equiv="Content-Type" content="text/html"/>
  521. """)
  522. testRender(
  523. "`meta`, `img`, `link`, `script`, `br`, and `hr` tags are closed by default.",
  524. """
  525. %br/
  526. %meta{"http-equiv" => "Content-Type", :content => "text/html"}/
  527. """, """
  528. <br/>
  529. <meta http-equiv="Content-Type" content="text/html"/>
  530. """)
  531. /////////////////////////////////////////////////////////////////////
  532. //
  533. // HTML Elements : Whitespace Removal: `>` and `<`
  534. //
  535. /////////////////////////////////////////////////////////////////////
  536. testRender(
  537. "'`<` will remove all whitespace immediately within a tag.",
  538. """
  539. %blockquote<
  540. %div
  541. Foo!
  542. """, """
  543. <blockquote><div>
  544. Foo!
  545. </div></blockquote>
  546. """)
  547. testRender(
  548. "`>` will remove all whitespace surrounding a tag",
  549. """
  550. %img/
  551. %img>/
  552. %img/
  553. """, """
  554. <img/><img/><img/>
  555. """)
  556. testRender(
  557. "`<` will remove all whitespace surrounding a rendered expression",
  558. """
  559. %p<= "Foo\nBar"
  560. """, """
  561. <p>Foo
  562. Bar</p>
  563. """)
  564. testRender(
  565. "'%tag><' trims the whitespace surrounding the tag and wrapping nested content'",
  566. """
  567. %img/
  568. %pre><
  569. foo
  570. bar
  571. %img/
  572. """, """
  573. <img/><pre>foo
  574. bar</pre><img/>
  575. """)
  576. testRender(
  577. "'%tag<>' trims the whitespace surrounding the tag and wrapping nested content'",
  578. """
  579. %img/
  580. %pre><
  581. foo
  582. bar
  583. %img/
  584. """, """
  585. <img/><pre>foo
  586. bar</pre><img/>
  587. """)
  588. /////////////////////////////////////////////////////////////////////
  589. //
  590. // Doctype: !!!
  591. //
  592. /////////////////////////////////////////////////////////////////////
  593. testRender(
  594. "you can have a document type or XML prolog generated automatically by including the characters !!!",
  595. """
  596. !!! XML
  597. !!!
  598. %html
  599. %head
  600. %title Myspace
  601. %body
  602. %h1 I am the international space station
  603. %p Sign my guestbook
  604. """, """
  605. <?xml version="1.0" encoding="utf-8" ?>
  606. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  607. <html>
  608. <head>
  609. <title>Myspace</title>
  610. </head>
  611. <body>
  612. <h1>I am the international space station</h1>
  613. <p>Sign my guestbook</p>
  614. </body>
  615. </html>
  616. """)
  617. /////////////////////////////////////////////////////////////////////
  618. //
  619. // Comments
  620. //
  621. /////////////////////////////////////////////////////////////////////
  622. testRender(
  623. "The `/`, when placed at the beginning of a line, wraps all text after it in an HTML comment",
  624. """
  625. %peanutbutterjelly
  626. / This is the peanutbutterjelly element
  627. I like sandwiches!
  628. """, """
  629. <peanutbutterjelly>
  630. <!-- This is the peanutbutterjelly element -->
  631. I like sandwiches!
  632. </peanutbutterjelly>
  633. """)
  634. testRender(
  635. "The `/` can also wrap indented sections of code.",
  636. """
  637. /
  638. %p This doesn't render...
  639. %div
  640. %h1 Because it's commented out!
  641. """, """
  642. <!--
  643. <p>This doesn't render...</p>
  644. <div>
  645. <h1>Because it's commented out!</h1>
  646. </div>
  647. -->
  648. """)
  649. testRender(
  650. "You can also use Internet Explorer conditional comments by enclosing the condition in square brackets",
  651. """
  652. /[if IE]
  653. %a{ :href => "http://www.mozilla.com/en-US/firefox/" }
  654. %h1 Get Firefox
  655. """, """
  656. <!--[if IE]>
  657. <a href="http://www.mozilla.com/en-US/firefox/">
  658. <h1>Get Firefox</h1>
  659. </a>
  660. <![endif]-->
  661. """)
  662. testRender(
  663. "`-#` signifies a silent comment.",
  664. """
  665. %p foo
  666. -# This is a comment
  667. %p bar
  668. """, """
  669. <p>foo</p>
  670. <p>bar</p>
  671. """)
  672. testRender(
  673. "You can also nest text beneath a `-#`",
  674. """
  675. %p foo
  676. -#
  677. This won't be displayed
  678. Nor will this
  679. %p bar
  680. """, """
  681. <p>foo</p>
  682. <p>bar</p>
  683. """)
  684. /////////////////////////////////////////////////////////////////////
  685. //
  686. // Scala Evaluation: Inserting Scala: `=`
  687. //
  688. /////////////////////////////////////////////////////////////////////
  689. testRender(
  690. "`=` is followed by Scala code. This code is evaluated and the output is inserted into the document.",
  691. """
  692. %p
  693. = List("hi", "there", "reader!").mkString(" ")
  694. = "yo"
  695. """, """
  696. <p>
  697. hi there reader!
  698. yo
  699. </p>
  700. """)
  701. testRender(
  702. "`=` can also be used at the end of a tag to insert Scala code within that tag.",
  703. """
  704. %p= "hello"
  705. """, """
  706. <p>hello</p>
  707. """)
  708. testRender(
  709. "'= var' expressions can acess implicitly bound variables",
  710. """
  711. %html
  712. %body
  713. = context.name
  714. """, """
  715. <html>
  716. <body>
  717. Hiram
  718. </body>
  719. </html>
  720. """)
  721. testRender(
  722. "'= var' expressions can access imported variables",
  723. """
  724. %html
  725. %body
  726. = name
  727. """, """
  728. <html>
  729. <body>
  730. Hiram
  731. </body>
  732. </html>
  733. """)
  734. testRender(
  735. "= on a NodeSeq is rendered unsanitized",
  736. """
  737. -@ val bean:Bean
  738. = bean.link
  739. """, """
  740. <a href="#size-10">red</a>
  741. """)
  742. testRender(
  743. "!= a NodeSeq is rendered unsanitized",
  744. """
  745. -@ val bean:Bean
  746. != bean.link
  747. """, """
  748. <a href="#size-10">red</a>
  749. """)
  750. // TODO should we do this???
  751. /*
  752. testRender("&= a NodeSeq is rendered sanitized",
  753. """
  754. -@ val bean:Bean
  755. &= bean.link
  756. ""","""
  757. &lt;a href=&quot;#size-10&quot;&gt;red&lt;/a&gt;
  758. """)
  759. */
  760. testRender(
  761. "&= a NodeSeq.toString is rendered sanitized",
  762. """
  763. -@ val bean:Bean
  764. &= bean.link.toString
  765. """, """
  766. &lt;a href=&quot;#size-10&quot;&gt;red&lt;/a&gt;
  767. """)
  768. /////////////////////////////////////////////////////////////////////
  769. //
  770. // Scala Evaluation: Running Scala: `-`
  771. //
  772. /////////////////////////////////////////////////////////////////////
  773. testRender(
  774. "`-` is followed by Scala code. The code is evaluated but *not* inserted into the document.",
  775. """
  776. - var foo = "hello"
  777. - foo += " there"
  778. - foo += " you!"
  779. %p= foo
  780. """, """
  781. <p>hello there you!</p>
  782. """)
  783. testRender(
  784. "`-` is followed by an indented Scala code block. The code is evaluated but *not* inserted into the document.",
  785. """
  786. -
  787. var foo = "hello"
  788. // note: you can use creative indentation in the block
  789. foo += " there"
  790. foo += " you!"
  791. %p= foo
  792. """, """
  793. <p>hello there you!</p>
  794. """)
  795. /////////////////////////////////////////////////////////////////////
  796. //
  797. // Scala Evaluation: Scala Blocks
  798. //
  799. /////////////////////////////////////////////////////////////////////
  800. testRender(
  801. "Nest block for case statements",
  802. """
  803. %p
  804. - 2 match
  805. - case 1 =>
  806. = "one"
  807. - case 2 =>
  808. = "two"
  809. - case 3 =>
  810. = "three"
  811. """, """
  812. <p>
  813. two
  814. </p>
  815. """)
  816. testRender(
  817. "Nest block for a `for` loop",
  818. """
  819. - for(i <- 42 to 46)
  820. %p= i
  821. %p See, I can count!
  822. """, """
  823. <p>42</p>
  824. <p>43</p>
  825. <p>44</p>
  826. <p>45</p>
  827. <p>46</p>
  828. <p>See, I can count!</p>
  829. """)
  830. testRender(
  831. "Passing partial funtions.",
  832. """
  833. %p
  834. = List(1,2,3).foldLeft("result: ")
  835. - (a,x)=>
  836. - a+x
  837. """, """
  838. <p>
  839. result: 123
  840. </p>
  841. """)
  842. testRender(
  843. "loop constructs don't need {} ",
  844. """
  845. %ol
  846. %li start
  847. - for( i <- 1 to 3 )
  848. - val message = "Hi "+i
  849. %li= message
  850. %li end
  851. """, """
  852. <ol>
  853. <li>start</li>
  854. <li>Hi 1</li>
  855. <li>Hi 2</li>
  856. <li>Hi 3</li>
  857. <li>end</li>
  858. </ol>
  859. """)
  860. testRender(
  861. "if / else constructs",
  862. """
  863. - if ( 1==2 )
  864. %p alternate reality
  865. - else
  866. %p still on earth
  867. """, """
  868. <p>still on earth</p>
  869. """)
  870. testRender(
  871. "try / catch constructs",
  872. """
  873. - try
  874. %p in try
  875. - throw new IllegalStateException()
  876. - catch
  877. - case e:IllegalStateException =>
  878. %p got the expected error
  879. - case e:Exception =>
  880. %p some odd error occurred
  881. """, """
  882. <p>in try</p>
  883. <p>got the expected error</p>
  884. """)
  885. /////////////////////////////////////////////////////////////////////
  886. //
  887. // Scala Interpolation: `#{}`
  888. //
  889. /////////////////////////////////////////////////////////////////////
  890. testRender(
  891. "Scala code can be interpolated within plain text using `#{}`",
  892. """
  893. %p This is #{quality} cake!
  894. """, """
  895. <p>This is scrumptious cake!</p>
  896. """)
  897. testRender(
  898. "Backslashes can be used to escape `#{` strings, but they don't act as escapes anywhere else in the string.",
  899. """
  900. %p
  901. A slash make a difference here: \#{name} is set to: \\#{name}
  902. But is ignored for: \# or \\
  903. """, """
  904. <p>
  905. A slash make a difference here: #{name} is set to: \Hiram
  906. But is ignored for: \# or \\
  907. </p>
  908. """)
  909. /////////////////////////////////////////////////////////////////////
  910. //
  911. // Escaping HTML: `&=`
  912. //
  913. /////////////////////////////////////////////////////////////////////
  914. testRender(
  915. "'&= expression' sanitizes the rendered expression",
  916. """
  917. &= "I like cheese & crackers"
  918. """, """
  919. I like cheese &amp; crackers
  920. """)
  921. testRender(
  922. "'& text' santizes interpolated expressions",
  923. """
  924. & I like #{"cheese & crackers"}
  925. """, """
  926. I like cheese &amp; crackers
  927. """)
  928. testRender(
  929. "'= expression' is sanitized by default",
  930. """
  931. = "I feel <strong>!"
  932. """, """
  933. I feel &lt;strong&gt;!
  934. """)
  935. /////////////////////////////////////////////////////////////////////
  936. //
  937. // Unescaping HTML: `!=`
  938. //
  939. /////////////////////////////////////////////////////////////////////
  940. testRender(
  941. "'!= expression' does not santize the rendered expression",
  942. """
  943. != "I feel <strong>!"
  944. """, """
  945. I feel <strong>!
  946. """)
  947. testRender(
  948. "'! text' does not santize interpolated expressions",
  949. """
  950. ! I feel #{"<strong>"}!
  951. """, """
  952. I feel <strong>!
  953. """)
  954. testRender(
  955. "'-@ val' makes an attribute accessibe as variable",
  956. """
  957. -@ val bean:Bean
  958. The bean is #{bean.color}
  959. """, """
  960. The bean is red
  961. """)
  962. testRender(
  963. "'-@ import val' makes an attribute's members accessibe as variables",
  964. """
  965. -@ import val bean:Bean
  966. The bean is #{color}
  967. """, """
  968. The bean is red
  969. """)
  970. testRender(
  971. "'-@ val name:type = expression' can specify a default value if the named attribute is not set",
  972. """
  973. -@ val doesnotexist:Bean = Bean("blue", 5)
  974. The bean is #{doesnotexist.color}
  975. """, """
  976. The bean is blue
  977. """)
  978. testRender(
  979. "'-@ val' can be used in nested tags",
  980. """
  981. %html
  982. test
  983. -@ val bean:Bean
  984. The bean is #{bean.color}
  985. """, """
  986. <html>
  987. test
  988. The bean is red
  989. </html>
  990. """)
  991. /////////////////////////////////////////////////////////////////////
  992. //
  993. // Preserving White Space
  994. //
  995. /////////////////////////////////////////////////////////////////////
  996. testRender(
  997. "Generated content in dynamic expression are properly indented.",
  998. """
  999. %html
  1000. %p
  1001. = "line1\nline2\nline3"
  1002. """, """
  1003. <html>
  1004. <p>
  1005. line1
  1006. line2
  1007. line3
  1008. </p>
  1009. </html>
  1010. """)
  1011. testRender(
  1012. "`~` preserves white space",
  1013. """
  1014. %html
  1015. %p
  1016. ~ "line1\nline2\nline3"
  1017. """, """
  1018. <html>
  1019. <p>
  1020. line1&#x000A;line2&#x000A;line3
  1021. </p>
  1022. </html>
  1023. """)
  1024. testRender(
  1025. "`&~` preserves and sanitizes",
  1026. """
  1027. &~ "<tag>\n</tag>"
  1028. """, """
  1029. &lt;tag&gt;&#x000A;&lt;/tag&gt;
  1030. """)
  1031. testRender(
  1032. "`!~` preserves and does not sanitize",
  1033. """
  1034. !~ "<tag>\n</tag>"
  1035. """, """
  1036. <tag>&#x000A;</tag>
  1037. """)
  1038. testRender(
  1039. "`~~` ugly preserves white space. values are not indented at all.",
  1040. """
  1041. %html
  1042. %p
  1043. ~~ "line1\nline2\nline3"
  1044. """, """
  1045. <html>
  1046. <p>
  1047. line1
  1048. line2
  1049. line3
  1050. </p>
  1051. </html>
  1052. """)
  1053. testRender(
  1054. "If the ScamlOptions.ugly option is enabled, then `=` behaves like `~~`",
  1055. """
  1056. %html
  1057. %p
  1058. = "line1\nline2\nline3"
  1059. """, """
  1060. <html>
  1061. <p>
  1062. line1
  1063. line2
  1064. line3
  1065. </p>
  1066. </html>
  1067. """, { () =>
  1068. ScamlOptions.ugly = true
  1069. }, { () =>
  1070. ScamlOptions.ugly = ScamlOptions.DEFAULT_UGLY
  1071. })
  1072. /////////////////////////////////////////////////////////////////////
  1073. //
  1074. // Indent Handling..
  1075. // You should enable white space viewing in your editor before touching this
  1076. // section.
  1077. //
  1078. /////////////////////////////////////////////////////////////////////
  1079. testRender(
  1080. "Scaml eats empty lines",
  1081. """
  1082. %ul
  1083. %li item 1
  1084. %ul
  1085. %li item 1
  1086. %li item 2
  1087. %li item 2
  1088. %p test
  1089. """, """
  1090. <ul>
  1091. <li>item 1</li>
  1092. <ul>
  1093. <li>item 1</li>
  1094. <li>item 2</li>
  1095. </ul>
  1096. <li>item 2</li>
  1097. </ul>
  1098. <p>test</p>
  1099. """)
  1100. testRender(
  1101. "Scaml does not eat empty lines in a filter",
  1102. """
  1103. :plain
  1104. item 1
  1105. item 2
  1106. %p test
  1107. """, """
  1108. item 1
  1109. item 2
  1110. <p>test</p>
  1111. """)
  1112. }
  1113. case class Bean(color: String, size: Int) {
  1114. def link = {
  1115. <a href={ "#size-" + size }>{ color }</a>
  1116. }
  1117. }