PageRenderTime 66ms CodeModel.GetById 13ms app.highlight 43ms RepoModel.GetById 1ms app.codeStats 0ms

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