honkster-haml 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. data/FAQ +138 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +332 -0
  4. data/Rakefile +198 -0
  5. data/VERSION.yml +4 -0
  6. data/bin/css2sass +7 -0
  7. data/bin/haml +9 -0
  8. data/bin/html2haml +7 -0
  9. data/bin/sass +8 -0
  10. data/extra/haml-mode.el +596 -0
  11. data/extra/sass-mode.el +137 -0
  12. data/init.rb +8 -0
  13. data/lib/haml/buffer.rb +255 -0
  14. data/lib/haml/engine.rb +268 -0
  15. data/lib/haml/error.rb +22 -0
  16. data/lib/haml/exec.rb +395 -0
  17. data/lib/haml/filters.rb +276 -0
  18. data/lib/haml/helpers/action_view_extensions.rb +45 -0
  19. data/lib/haml/helpers/action_view_mods.rb +181 -0
  20. data/lib/haml/helpers.rb +468 -0
  21. data/lib/haml/html.rb +218 -0
  22. data/lib/haml/precompiler.rb +901 -0
  23. data/lib/haml/shared.rb +45 -0
  24. data/lib/haml/template/patch.rb +58 -0
  25. data/lib/haml/template/plugin.rb +72 -0
  26. data/lib/haml/template.rb +51 -0
  27. data/lib/haml/util.rb +77 -0
  28. data/lib/haml/version.rb +25 -0
  29. data/lib/haml.rb +1043 -0
  30. data/lib/sass/css.rb +388 -0
  31. data/lib/sass/engine.rb +499 -0
  32. data/lib/sass/environment.rb +33 -0
  33. data/lib/sass/error.rb +35 -0
  34. data/lib/sass/plugin/merb.rb +56 -0
  35. data/lib/sass/plugin/rails.rb +24 -0
  36. data/lib/sass/plugin.rb +203 -0
  37. data/lib/sass/repl.rb +51 -0
  38. data/lib/sass/script/bool.rb +13 -0
  39. data/lib/sass/script/color.rb +97 -0
  40. data/lib/sass/script/funcall.rb +28 -0
  41. data/lib/sass/script/functions.rb +122 -0
  42. data/lib/sass/script/lexer.rb +152 -0
  43. data/lib/sass/script/literal.rb +80 -0
  44. data/lib/sass/script/number.rb +231 -0
  45. data/lib/sass/script/operation.rb +30 -0
  46. data/lib/sass/script/parser.rb +142 -0
  47. data/lib/sass/script/string.rb +18 -0
  48. data/lib/sass/script/unary_operation.rb +21 -0
  49. data/lib/sass/script/variable.rb +20 -0
  50. data/lib/sass/script.rb +38 -0
  51. data/lib/sass/tree/attr_node.rb +64 -0
  52. data/lib/sass/tree/comment_node.rb +34 -0
  53. data/lib/sass/tree/debug_node.rb +22 -0
  54. data/lib/sass/tree/directive_node.rb +50 -0
  55. data/lib/sass/tree/file_node.rb +27 -0
  56. data/lib/sass/tree/for_node.rb +29 -0
  57. data/lib/sass/tree/if_node.rb +27 -0
  58. data/lib/sass/tree/mixin_def_node.rb +18 -0
  59. data/lib/sass/tree/mixin_node.rb +34 -0
  60. data/lib/sass/tree/node.rb +99 -0
  61. data/lib/sass/tree/rule_node.rb +120 -0
  62. data/lib/sass/tree/variable_node.rb +24 -0
  63. data/lib/sass/tree/while_node.rb +20 -0
  64. data/lib/sass.rb +1062 -0
  65. data/rails/init.rb +1 -0
  66. data/test/benchmark.rb +99 -0
  67. data/test/haml/engine_test.rb +782 -0
  68. data/test/haml/helper_test.rb +224 -0
  69. data/test/haml/html2haml_test.rb +92 -0
  70. data/test/haml/markaby/standard.mab +52 -0
  71. data/test/haml/mocks/article.rb +6 -0
  72. data/test/haml/results/content_for_layout.xhtml +15 -0
  73. data/test/haml/results/eval_suppressed.xhtml +9 -0
  74. data/test/haml/results/filters.xhtml +62 -0
  75. data/test/haml/results/helpers.xhtml +93 -0
  76. data/test/haml/results/helpful.xhtml +10 -0
  77. data/test/haml/results/just_stuff.xhtml +68 -0
  78. data/test/haml/results/list.xhtml +12 -0
  79. data/test/haml/results/nuke_inner_whitespace.xhtml +40 -0
  80. data/test/haml/results/nuke_outer_whitespace.xhtml +148 -0
  81. data/test/haml/results/original_engine.xhtml +20 -0
  82. data/test/haml/results/partial_layout.xhtml +5 -0
  83. data/test/haml/results/partials.xhtml +21 -0
  84. data/test/haml/results/render_layout.xhtml +3 -0
  85. data/test/haml/results/silent_script.xhtml +74 -0
  86. data/test/haml/results/standard.xhtml +42 -0
  87. data/test/haml/results/tag_parsing.xhtml +23 -0
  88. data/test/haml/results/very_basic.xhtml +5 -0
  89. data/test/haml/results/whitespace_handling.xhtml +89 -0
  90. data/test/haml/rhtml/_av_partial_1.rhtml +12 -0
  91. data/test/haml/rhtml/_av_partial_2.rhtml +8 -0
  92. data/test/haml/rhtml/action_view.rhtml +62 -0
  93. data/test/haml/rhtml/standard.rhtml +54 -0
  94. data/test/haml/template_test.rb +204 -0
  95. data/test/haml/templates/_av_partial_1.haml +9 -0
  96. data/test/haml/templates/_av_partial_1_ugly.haml +9 -0
  97. data/test/haml/templates/_av_partial_2.haml +5 -0
  98. data/test/haml/templates/_av_partial_2_ugly.haml +5 -0
  99. data/test/haml/templates/_layout.erb +3 -0
  100. data/test/haml/templates/_layout_for_partial.haml +3 -0
  101. data/test/haml/templates/_partial.haml +8 -0
  102. data/test/haml/templates/_text_area.haml +3 -0
  103. data/test/haml/templates/action_view.haml +47 -0
  104. data/test/haml/templates/action_view_ugly.haml +47 -0
  105. data/test/haml/templates/breakage.haml +8 -0
  106. data/test/haml/templates/content_for_layout.haml +10 -0
  107. data/test/haml/templates/eval_suppressed.haml +11 -0
  108. data/test/haml/templates/filters.haml +66 -0
  109. data/test/haml/templates/helpers.haml +95 -0
  110. data/test/haml/templates/helpful.haml +11 -0
  111. data/test/haml/templates/just_stuff.haml +83 -0
  112. data/test/haml/templates/list.haml +12 -0
  113. data/test/haml/templates/nuke_inner_whitespace.haml +32 -0
  114. data/test/haml/templates/nuke_outer_whitespace.haml +144 -0
  115. data/test/haml/templates/original_engine.haml +17 -0
  116. data/test/haml/templates/partial_layout.haml +3 -0
  117. data/test/haml/templates/partialize.haml +1 -0
  118. data/test/haml/templates/partials.haml +12 -0
  119. data/test/haml/templates/render_layout.haml +2 -0
  120. data/test/haml/templates/silent_script.haml +40 -0
  121. data/test/haml/templates/standard.haml +42 -0
  122. data/test/haml/templates/standard_ugly.haml +1 -0
  123. data/test/haml/templates/tag_parsing.haml +21 -0
  124. data/test/haml/templates/very_basic.haml +4 -0
  125. data/test/haml/templates/whitespace_handling.haml +87 -0
  126. data/test/linked_rails.rb +12 -0
  127. data/test/sass/css2sass_test.rb +193 -0
  128. data/test/sass/engine_test.rb +655 -0
  129. data/test/sass/functions_test.rb +96 -0
  130. data/test/sass/more_results/more1.css +9 -0
  131. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  132. data/test/sass/more_results/more_import.css +29 -0
  133. data/test/sass/more_templates/_more_partial.sass +2 -0
  134. data/test/sass/more_templates/more1.sass +23 -0
  135. data/test/sass/more_templates/more_import.sass +11 -0
  136. data/test/sass/plugin_test.rb +208 -0
  137. data/test/sass/results/alt.css +4 -0
  138. data/test/sass/results/basic.css +9 -0
  139. data/test/sass/results/compact.css +5 -0
  140. data/test/sass/results/complex.css +87 -0
  141. data/test/sass/results/compressed.css +1 -0
  142. data/test/sass/results/expanded.css +19 -0
  143. data/test/sass/results/import.css +29 -0
  144. data/test/sass/results/line_numbers.css +49 -0
  145. data/test/sass/results/mixins.css +95 -0
  146. data/test/sass/results/multiline.css +24 -0
  147. data/test/sass/results/nested.css +22 -0
  148. data/test/sass/results/parent_ref.css +13 -0
  149. data/test/sass/results/script.css +16 -0
  150. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  151. data/test/sass/results/subdir/subdir.css +3 -0
  152. data/test/sass/results/units.css +11 -0
  153. data/test/sass/script_test.rb +250 -0
  154. data/test/sass/templates/_partial.sass +2 -0
  155. data/test/sass/templates/alt.sass +16 -0
  156. data/test/sass/templates/basic.sass +23 -0
  157. data/test/sass/templates/bork.sass +2 -0
  158. data/test/sass/templates/bork2.sass +2 -0
  159. data/test/sass/templates/compact.sass +17 -0
  160. data/test/sass/templates/complex.sass +309 -0
  161. data/test/sass/templates/compressed.sass +15 -0
  162. data/test/sass/templates/expanded.sass +17 -0
  163. data/test/sass/templates/import.sass +11 -0
  164. data/test/sass/templates/importee.sass +19 -0
  165. data/test/sass/templates/line_numbers.sass +13 -0
  166. data/test/sass/templates/mixins.sass +76 -0
  167. data/test/sass/templates/multiline.sass +20 -0
  168. data/test/sass/templates/nested.sass +25 -0
  169. data/test/sass/templates/parent_ref.sass +25 -0
  170. data/test/sass/templates/script.sass +101 -0
  171. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  172. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  173. data/test/sass/templates/subdir/subdir.sass +6 -0
  174. data/test/sass/templates/units.sass +11 -0
  175. data/test/test_helper.rb +21 -0
  176. metadata +272 -0
@@ -0,0 +1,782 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../test_helper'
3
+
4
+ class EngineTest < Test::Unit::TestCase
5
+ # A map of erroneous Haml documents to the error messages they should produce.
6
+ # The error messages may be arrays;
7
+ # if so, the second element should be the line number that should be reported for the error.
8
+ # If this isn't provided, the tests will assume the line number should be the last line of the document.
9
+ EXCEPTION_MAP = {
10
+ "!!!\n a" => "Illegal nesting: nesting within a header command is illegal.",
11
+ "a\n b" => "Illegal nesting: nesting within plain text is illegal.",
12
+ "/ a\n b" => "Illegal nesting: nesting within a tag that already has content is illegal.",
13
+ "% a" => 'Invalid tag: "% a".',
14
+ "%p a\n b" => "Illegal nesting: content can't be both given on the same line as %p and nested within it.",
15
+ "%p=" => "There's no Ruby code for = to evaluate.",
16
+ "%p~" => "There's no Ruby code for ~ to evaluate.",
17
+ "~" => "There's no Ruby code for ~ to evaluate.",
18
+ "=" => "There's no Ruby code for = to evaluate.",
19
+ "%p/\n a" => "Illegal nesting: nesting within a self-closing tag is illegal.",
20
+ ":a\n b" => ['Filter "a" is not defined.', 1],
21
+ ":a= b" => 'Invalid filter name ":a= b".',
22
+ "." => "Illegal element: classes and ids must have values.",
23
+ ".#" => "Illegal element: classes and ids must have values.",
24
+ ".{} a" => "Illegal element: classes and ids must have values.",
25
+ ".= a" => "Illegal element: classes and ids must have values.",
26
+ "%p..a" => "Illegal element: classes and ids must have values.",
27
+ "%a/ b" => "Self-closing tags can't have content.",
28
+ "%p{:a => 'b',\n:c => 'd'}/ e" => ["Self-closing tags can't have content.", 2],
29
+ "%p{:a => 'b',\n:c => 'd'}=" => ["There's no Ruby code for = to evaluate.", 2],
30
+ "%p.{:a => 'b',\n:c => 'd'} e" => ["Illegal element: classes and ids must have values.", 1],
31
+ "%p{:a => 'b',\n:c => 'd',\n:e => 'f'}\n%p/ a" => ["Self-closing tags can't have content.", 4],
32
+ "%p{:a => 'b',\n:c => 'd',\n:e => 'f'}\n- raise 'foo'" => ["foo", 4],
33
+ "%p{:a => 'b',\n:c => raise('foo'),\n:e => 'f'}" => ["foo", 2],
34
+ "%p{:a => 'b',\n:c => 'd',\n:e => raise('foo')}" => ["foo", 3],
35
+ " %p foo" => "Indenting at the beginning of the document is illegal.",
36
+ " %p foo" => "Indenting at the beginning of the document is illegal.",
37
+ "- end" => "You don't need to use \"- end\" in Haml. Use indentation instead:\n- if foo?\n %strong Foo!\n- else\n Not foo.",
38
+ " \n\t\n %p foo" => ["Indenting at the beginning of the document is illegal.", 3],
39
+ "\n\n %p foo" => ["Indenting at the beginning of the document is illegal.", 3],
40
+ "%p\n foo\n foo" => ["Inconsistent indentation: 1 space was used for indentation, but the rest of the document was indented using 2 spaces.", 3],
41
+ "%p\n foo\n%p\n foo" => ["Inconsistent indentation: 1 space was used for indentation, but the rest of the document was indented using 2 spaces.", 4],
42
+ "%p\n\t\tfoo\n\tfoo" => ["Inconsistent indentation: 1 tab was used for indentation, but the rest of the document was indented using 2 tabs.", 3],
43
+ "%p\n foo\n foo" => ["Inconsistent indentation: 3 spaces were used for indentation, but the rest of the document was indented using 2 spaces.", 3],
44
+ "%p\n foo\n %p\n bar" => ["Inconsistent indentation: 3 spaces were used for indentation, but the rest of the document was indented using 2 spaces.", 4],
45
+ "%p\n :plain\n bar\n \t baz" => ['Inconsistent indentation: " \t " was used for indentation, but the rest of the document was indented using 2 spaces.', 4],
46
+ "%p\n foo\n%p\n bar" => ["The line was indented 2 levels deeper than the previous line.", 4],
47
+ "%p\n foo\n %p\n bar" => ["The line was indented 3 levels deeper than the previous line.", 4],
48
+ "%p\n \tfoo" => ["Indentation can't use both tabs and spaces.", 2],
49
+
50
+ # Regression tests
51
+ "- raise 'foo'\n\n\n\nbar" => ["foo", 1],
52
+ "= 'foo'\n-raise 'foo'" => ["foo", 2],
53
+ "\n\n\n- raise 'foo'" => ["foo", 4],
54
+ "%p foo |\n bar |\n baz |\nbop\n- raise 'foo'" => ["foo", 5],
55
+ "foo\n\n\n bar" => ["Illegal nesting: nesting within plain text is illegal.", 4],
56
+ "%p/\n\n bar" => ["Illegal nesting: nesting within a self-closing tag is illegal.", 3],
57
+ "%p foo\n\n bar" => ["Illegal nesting: content can't be both given on the same line as %p and nested within it.", 3],
58
+ "/ foo\n\n bar" => ["Illegal nesting: nesting within a tag that already has content is illegal.", 3],
59
+ "!!!\n\n bar" => ["Illegal nesting: nesting within a header command is illegal.", 3],
60
+ "foo\n:ruby\n 1\n 2\n 3\n- raise 'foo'" => ["foo", 6],
61
+ }
62
+
63
+ User = Struct.new('User', :id)
64
+
65
+ def render(text, options = {}, &block)
66
+ scope = options.delete(:scope) || Object.new
67
+ locals = options.delete(:locals) || {}
68
+ engine(text, options).to_html(scope, locals, &block)
69
+ end
70
+
71
+ def engine(text, options = {})
72
+ unless options[:filename]
73
+ # use caller method name as fake filename. useful for debugging
74
+ i = -1
75
+ caller[i+=1] =~ /`(.+?)'/ until $1 and $1.index('test_') == 0
76
+ options[:filename] = "(#{$1})"
77
+ end
78
+ Haml::Engine.new(text, options)
79
+ end
80
+
81
+ def test_empty_render
82
+ assert_equal "", render("")
83
+ end
84
+
85
+ def test_flexible_tabulation
86
+ assert_equal("<p>\n foo\n</p>\n<q>\n bar\n <a>\n baz\n </a>\n</q>\n",
87
+ render("%p\n foo\n%q\n bar\n %a\n baz"))
88
+ assert_equal("<p>\n foo\n</p>\n<q>\n bar\n <a>\n baz\n </a>\n</q>\n",
89
+ render("%p\n\tfoo\n%q\n\tbar\n\t%a\n\t\tbaz"))
90
+ assert_equal("<p>\n \t \t bar\n baz\n</p>\n",
91
+ render("%p\n :plain\n \t \t bar\n baz"))
92
+ end
93
+
94
+ def test_empty_render_should_remain_empty
95
+ assert_equal('', render(''))
96
+ end
97
+
98
+ def test_attributes_should_render_correctly
99
+ assert_equal("<div class='atlantis' style='ugly'></div>", render(".atlantis{:style => 'ugly'}").chomp)
100
+ end
101
+
102
+ def test_ruby_code_should_work_inside_attributes
103
+ author = 'hcatlin'
104
+ assert_equal("<p class='3'>foo</p>", render("%p{:class => 1+2} foo").chomp)
105
+ end
106
+
107
+ def test_nil_should_render_empty_tag
108
+ assert_equal("<div class='no_attributes'></div>",
109
+ render(".no_attributes{:nil => nil}").chomp)
110
+ end
111
+
112
+ def test_strings_should_get_stripped_inside_tags
113
+ assert_equal("<div class='stripped'>This should have no spaces in front of it</div>",
114
+ render(".stripped This should have no spaces in front of it").chomp)
115
+ end
116
+
117
+ def test_one_liner_should_be_one_line
118
+ assert_equal("<p>Hello</p>", render('%p Hello').chomp)
119
+ end
120
+
121
+ def test_one_liner_with_newline_shouldnt_be_one_line
122
+ assert_equal("<p>\n foo\n bar\n</p>", render('%p= "foo\nbar"').chomp)
123
+ end
124
+
125
+ def test_multi_render
126
+ engine = engine("%strong Hi there!")
127
+ assert_equal("<strong>Hi there!</strong>\n", engine.to_html)
128
+ assert_equal("<strong>Hi there!</strong>\n", engine.to_html)
129
+ assert_equal("<strong>Hi there!</strong>\n", engine.to_html)
130
+ end
131
+
132
+ def test_double_equals
133
+ assert_equal("<p>Hello World</p>\n", render('%p== Hello #{who}', :locals => {:who => 'World'}))
134
+ assert_equal("<p>\n Hello World\n</p>\n", render("%p\n == Hello \#{who}", :locals => {:who => 'World'}))
135
+ end
136
+
137
+ def test_double_equals_in_the_middle_of_a_string
138
+ assert_equal("\"title 'Title'. \"\n",
139
+ render("== \"title '\#{\"Title\"}'. \""))
140
+ end
141
+
142
+ def test_nil_tag_value_should_render_as_empty
143
+ assert_equal("<p></p>\n", render("%p= nil"))
144
+ end
145
+
146
+ def test_tag_with_failed_if_should_render_as_empty
147
+ assert_equal("<p></p>\n", render("%p= 'Hello' if false"))
148
+ end
149
+
150
+ def test_static_attributes_with_empty_attr
151
+ assert_equal("<img alt='' src='/foo.png' />\n", render("%img{:src => '/foo.png', :alt => ''}"))
152
+ end
153
+
154
+ def test_dynamic_attributes_with_empty_attr
155
+ assert_equal("<img alt='' src='/foo.png' />\n", render("%img{:width => nil, :src => '/foo.png', :alt => String.new}"))
156
+ end
157
+
158
+ def test_attribute_hash_with_newlines
159
+ assert_equal("<p a='b' c='d'>foop</p>\n", render("%p{:a => 'b',\n :c => 'd'} foop"))
160
+ assert_equal("<p a='b' c='d'>\n foop\n</p>\n", render("%p{:a => 'b',\n :c => 'd'}\n foop"))
161
+ assert_equal("<p a='b' c='d' />\n", render("%p{:a => 'b',\n :c => 'd'}/"))
162
+ assert_equal("<p a='b' c='d' e='f'></p>\n", render("%p{:a => 'b',\n :c => 'd',\n :e => 'f'}"))
163
+ end
164
+
165
+ def test_attr_hashes_not_modified
166
+ hash = {:color => 'red'}
167
+ assert_equal(<<HTML, render(<<HAML, :locals => {:hash => hash}))
168
+ <div color='red'></div>
169
+ <div class='special' color='red'></div>
170
+ <div color='red'></div>
171
+ HTML
172
+ %div{hash}
173
+ .special{hash}
174
+ %div{hash}
175
+ HAML
176
+ assert_equal(hash, {:color => 'red'})
177
+ end
178
+
179
+ def test_end_of_file_multiline
180
+ assert_equal("<p>0</p>\n<p>1</p>\n<p>2</p>\n", render("- for i in (0...3)\n %p= |\n i |"))
181
+ end
182
+
183
+ def test_cr_newline
184
+ assert_equal("<p>foo</p>\n<p>bar</p>\n<p>baz</p>\n<p>boom</p>\n", render("%p foo\r%p bar\r\n%p baz\n\r%p boom"))
185
+ end
186
+
187
+ def test_textareas
188
+ assert_equal("<textarea>Foo&#x000A; bar&#x000A; baz</textarea>\n",
189
+ render('%textarea= "Foo\n bar\n baz"'))
190
+
191
+ assert_equal("<pre>Foo&#x000A; bar&#x000A; baz</pre>\n",
192
+ render('%pre= "Foo\n bar\n baz"'))
193
+
194
+ assert_equal("<textarea>#{'a' * 100}</textarea>\n",
195
+ render("%textarea #{'a' * 100}"))
196
+
197
+ assert_equal("<p>\n <textarea>Foo\n Bar\n Baz</textarea>\n</p>\n", render(<<SOURCE))
198
+ %p
199
+ %textarea
200
+ Foo
201
+ Bar
202
+ Baz
203
+ SOURCE
204
+ end
205
+
206
+ def test_boolean_attributes
207
+ assert_equal("<p bar baz='true' foo='bar'></p>\n",
208
+ render("%p{:foo => 'bar', :bar => true, :baz => 'true'}", :format => :html4))
209
+ assert_equal("<p bar='bar' baz='true' foo='bar'></p>\n",
210
+ render("%p{:foo => 'bar', :bar => true, :baz => 'true'}", :format => :xhtml))
211
+
212
+ assert_equal("<p baz='false' foo='bar'></p>\n",
213
+ render("%p{:foo => 'bar', :bar => false, :baz => 'false'}", :format => :html4))
214
+ assert_equal("<p baz='false' foo='bar'></p>\n",
215
+ render("%p{:foo => 'bar', :bar => false, :baz => 'false'}", :format => :xhtml))
216
+ end
217
+
218
+ def test_both_whitespace_nukes_work_together
219
+ assert_equal(<<RESULT, render(<<SOURCE))
220
+ <p><q>Foo
221
+ Bar</q></p>
222
+ RESULT
223
+ %p
224
+ %q><= "Foo\\nBar"
225
+ SOURCE
226
+ end
227
+
228
+ # Regression tests
229
+
230
+ def test_whitespace_nuke_with_both_newlines
231
+ assert_equal("<p>foo</p>\n", render('%p<= "\nfoo\n"'))
232
+ assert_equal(<<HTML, render(<<HAML))
233
+ <p>
234
+ <p>foo</p>
235
+ </p>
236
+ HTML
237
+ %p
238
+ %p<= "\\nfoo\\n"
239
+ HAML
240
+ end
241
+
242
+ def test_both_case_indentation_work_with_deeply_nested_code
243
+ result = <<RESULT
244
+ <h2>
245
+ other
246
+ </h2>
247
+ RESULT
248
+ assert_equal(result, render(<<HAML))
249
+ - case 'other'
250
+ - when 'test'
251
+ %h2
252
+ hi
253
+ - when 'other'
254
+ %h2
255
+ other
256
+ HAML
257
+ assert_equal(result, render(<<HAML))
258
+ - case 'other'
259
+ - when 'test'
260
+ %h2
261
+ hi
262
+ - when 'other'
263
+ %h2
264
+ other
265
+ HAML
266
+ end
267
+
268
+ def test_equals_block_with_ugly
269
+ assert_equal("foo\n", render(<<HAML, :ugly => true))
270
+ = capture_haml do
271
+ foo
272
+ HAML
273
+ end
274
+
275
+ def test_plain_equals_with_ugly
276
+ assert_equal("foo\nbar\n", render(<<HAML, :ugly => true))
277
+ = "foo"
278
+ bar
279
+ HAML
280
+ end
281
+
282
+ def test_inline_if
283
+ assert_equal(<<HTML, render(<<HAML))
284
+ <p>One</p>
285
+ <p></p>
286
+ <p>Three</p>
287
+ HTML
288
+ - for name in ["One", "Two", "Three"]
289
+ %p= name unless name == "Two"
290
+ HAML
291
+ end
292
+
293
+ def test_end_with_method_call
294
+ assert_equal(<<HTML, render(<<HAML))
295
+ 2|3|4
296
+ b-a-r
297
+ HTML
298
+ = [1, 2, 3].map do |i|
299
+ - i + 1
300
+ - end.join("|")
301
+ = "bar".gsub(/./) do |s|
302
+ - s + "-"
303
+ - end.gsub(/-$/) do |s|
304
+ - ''
305
+ HAML
306
+ end
307
+
308
+ def test_multiline_with_colon_after_filter
309
+ assert_equal(<<HTML, render(<<HAML))
310
+ Foo
311
+ Bar
312
+ HTML
313
+ :plain
314
+ Foo
315
+ = { :a => "Bar", |
316
+ :b => "Baz" }[:a] |
317
+ HAML
318
+ assert_equal(<<HTML, render(<<HAML))
319
+
320
+ Bar
321
+ HTML
322
+ :plain
323
+ = { :a => "Bar", |
324
+ :b => "Baz" }[:a] |
325
+ HAML
326
+ end
327
+
328
+ def test_multiline_in_filter
329
+ assert_equal(<<HTML, render(<<HAML))
330
+ Foo |
331
+ Bar |
332
+ Baz
333
+ HTML
334
+ :plain
335
+ Foo |
336
+ Bar |
337
+ Baz
338
+ HAML
339
+ end
340
+
341
+ # HTML escaping tests
342
+
343
+ def test_ampersand_equals_should_escape
344
+ assert_equal("<p>\n foo &amp; bar\n</p>\n", render("%p\n &= 'foo & bar'", :escape_html => false))
345
+ end
346
+
347
+ def test_ampersand_equals_inline_should_escape
348
+ assert_equal("<p>foo &amp; bar</p>\n", render("%p&= 'foo & bar'", :escape_html => false))
349
+ end
350
+
351
+ def test_ampersand_equals_should_escape_before_preserve
352
+ assert_equal("<textarea>foo&#x000A;bar</textarea>\n", render('%textarea&= "foo\nbar"', :escape_html => false))
353
+ end
354
+
355
+ def test_bang_equals_should_not_escape
356
+ assert_equal("<p>\n foo & bar\n</p>\n", render("%p\n != 'foo & bar'", :escape_html => true))
357
+ end
358
+
359
+ def test_bang_equals_inline_should_not_escape
360
+ assert_equal("<p>foo & bar</p>\n", render("%p!= 'foo & bar'", :escape_html => true))
361
+ end
362
+
363
+ def test_static_attributes_should_be_escaped
364
+ assert_equal("<img class='atlantis' style='ugly&amp;stupid' />\n",
365
+ render("%img.atlantis{:style => 'ugly&stupid'}"))
366
+ assert_equal("<div class='atlantis' style='ugly&amp;stupid'>foo</div>\n",
367
+ render(".atlantis{:style => 'ugly&stupid'} foo"))
368
+ assert_equal("<p class='atlantis' style='ugly&amp;stupid'>foo</p>\n",
369
+ render("%p.atlantis{:style => 'ugly&stupid'}= 'foo'"))
370
+ assert_equal("<p class='atlantis' style='ugly&#x000A;stupid'></p>\n",
371
+ render("%p.atlantis{:style => \"ugly\\nstupid\"}"))
372
+ end
373
+
374
+ def test_dynamic_attributes_should_be_escaped
375
+ assert_equal("<img alt='' src='&amp;foo.png' />\n",
376
+ render("%img{:width => nil, :src => '&foo.png', :alt => String.new}"))
377
+ assert_equal("<p alt='' src='&amp;foo.png'>foo</p>\n",
378
+ render("%p{:width => nil, :src => '&foo.png', :alt => String.new} foo"))
379
+ assert_equal("<div alt='' src='&amp;foo.png'>foo</div>\n",
380
+ render("%div{:width => nil, :src => '&foo.png', :alt => String.new}= 'foo'"))
381
+ assert_equal("<img alt='' src='foo&#x000A;.png' />\n",
382
+ render("%img{:width => nil, :src => \"foo\\n.png\", :alt => String.new}"))
383
+ end
384
+
385
+ def test_string_double_equals_should_be_esaped
386
+ assert_equal("<p>4&amp;3</p>\n", render("%p== \#{2+2}&\#{2+1}", :escape_html => true))
387
+ assert_equal("<p>4&3</p>\n", render("%p== \#{2+2}&\#{2+1}", :escape_html => false))
388
+ end
389
+
390
+ def test_escaped_inline_string_double_equals
391
+ assert_equal("<p>4&amp;3</p>\n", render("%p&== \#{2+2}&\#{2+1}", :escape_html => true))
392
+ assert_equal("<p>4&amp;3</p>\n", render("%p&== \#{2+2}&\#{2+1}", :escape_html => false))
393
+ end
394
+
395
+ def test_unescaped_inline_string_double_equals
396
+ assert_equal("<p>4&3</p>\n", render("%p!== \#{2+2}&\#{2+1}", :escape_html => true))
397
+ assert_equal("<p>4&3</p>\n", render("%p!== \#{2+2}&\#{2+1}", :escape_html => false))
398
+ end
399
+
400
+ def test_escaped_string_double_equals
401
+ assert_equal("<p>\n 4&amp;3\n</p>\n", render("%p\n &== \#{2+2}&\#{2+1}", :escape_html => true))
402
+ assert_equal("<p>\n 4&amp;3\n</p>\n", render("%p\n &== \#{2+2}&\#{2+1}", :escape_html => false))
403
+ end
404
+
405
+ def test_unescaped_string_double_equals
406
+ assert_equal("<p>\n 4&3\n</p>\n", render("%p\n !== \#{2+2}&\#{2+1}", :escape_html => true))
407
+ assert_equal("<p>\n 4&3\n</p>\n", render("%p\n !== \#{2+2}&\#{2+1}", :escape_html => false))
408
+ end
409
+
410
+ def test_string_interpolation_should_be_esaped
411
+ assert_equal("<p>4&amp;3</p>\n", render("%p \#{2+2}&\#{2+1}", :escape_html => true))
412
+ assert_equal("<p>4&3</p>\n", render("%p \#{2+2}&\#{2+1}", :escape_html => false))
413
+ end
414
+
415
+ def test_escaped_inline_string_interpolation
416
+ assert_equal("<p>4&amp;3</p>\n", render("%p& \#{2+2}&\#{2+1}", :escape_html => true))
417
+ assert_equal("<p>4&amp;3</p>\n", render("%p& \#{2+2}&\#{2+1}", :escape_html => false))
418
+ end
419
+
420
+ def test_unescaped_inline_string_interpolation
421
+ assert_equal("<p>4&3</p>\n", render("%p! \#{2+2}&\#{2+1}", :escape_html => true))
422
+ assert_equal("<p>4&3</p>\n", render("%p! \#{2+2}&\#{2+1}", :escape_html => false))
423
+ end
424
+
425
+ def test_escaped_string_interpolation
426
+ assert_equal("<p>\n 4&amp;3\n</p>\n", render("%p\n & \#{2+2}&\#{2+1}", :escape_html => true))
427
+ assert_equal("<p>\n 4&amp;3\n</p>\n", render("%p\n & \#{2+2}&\#{2+1}", :escape_html => false))
428
+ end
429
+
430
+ def test_unescaped_string_interpolation
431
+ assert_equal("<p>\n 4&3\n</p>\n", render("%p\n ! \#{2+2}&\#{2+1}", :escape_html => true))
432
+ assert_equal("<p>\n 4&3\n</p>\n", render("%p\n ! \#{2+2}&\#{2+1}", :escape_html => false))
433
+ end
434
+
435
+ def test_scripts_should_respect_escape_html_option
436
+ assert_equal("<p>\n foo &amp; bar\n</p>\n", render("%p\n = 'foo & bar'", :escape_html => true))
437
+ assert_equal("<p>\n foo & bar\n</p>\n", render("%p\n = 'foo & bar'", :escape_html => false))
438
+ end
439
+
440
+ def test_inline_scripts_should_respect_escape_html_option
441
+ assert_equal("<p>foo &amp; bar</p>\n", render("%p= 'foo & bar'", :escape_html => true))
442
+ assert_equal("<p>foo & bar</p>\n", render("%p= 'foo & bar'", :escape_html => false))
443
+ end
444
+
445
+ def test_script_ending_in_comment_should_render_when_html_is_escaped
446
+ assert_equal("foo&amp;bar\n", render("= 'foo&bar' #comment", :escape_html => true))
447
+ end
448
+
449
+ def test_script_with_if_shouldnt_output
450
+ assert_equal(<<HTML, render(<<HAML))
451
+ <p>foo</p>
452
+ <p></p>
453
+ HTML
454
+ %p= "foo"
455
+ %p= "bar" if false
456
+ HAML
457
+ end
458
+
459
+ # Options tests
460
+
461
+ def test_filename_and_line
462
+ begin
463
+ render("\n\n = abc", :filename => 'test', :line => 2)
464
+ rescue Exception => e
465
+ assert_kind_of Haml::SyntaxError, e
466
+ assert_match(/test:4/, e.backtrace.first)
467
+ end
468
+
469
+ begin
470
+ render("\n\n= 123\n\n= nil[]", :filename => 'test', :line => 2)
471
+ rescue Exception => e
472
+ assert_kind_of NoMethodError, e
473
+ assert_match(/test:6/, e.backtrace.first)
474
+ end
475
+ end
476
+
477
+ def test_stop_eval
478
+ assert_equal("", render("= 'Hello'", :suppress_eval => true))
479
+ assert_equal("", render("- haml_concat 'foo'", :suppress_eval => true))
480
+ assert_equal("<div id='foo' yes='no' />\n", render("#foo{:yes => 'no'}/", :suppress_eval => true))
481
+ assert_equal("<div id='foo' />\n", render("#foo{:yes => 'no', :call => a_function() }/", :suppress_eval => true))
482
+ assert_equal("<div />\n", render("%div[1]/", :suppress_eval => true))
483
+ assert_equal("", render(":ruby\n Kernel.puts 'hello'", :suppress_eval => true))
484
+ end
485
+
486
+ def test_doctypes
487
+ assert_equal('<!DOCTYPE html>',
488
+ render('!!!', :format => :html5).strip)
489
+ assert_equal('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
490
+ render('!!! strict').strip)
491
+ assert_equal('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">',
492
+ render('!!! frameset').strip)
493
+ assert_equal('<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">',
494
+ render('!!! mobile').strip)
495
+ assert_equal('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">',
496
+ render('!!! basic').strip)
497
+ assert_equal('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
498
+ render('!!! transitional').strip)
499
+ assert_equal('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
500
+ render('!!!').strip)
501
+ assert_equal('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">',
502
+ render('!!! strict', :format => :html4).strip)
503
+ assert_equal('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">',
504
+ render('!!! frameset', :format => :html4).strip)
505
+ assert_equal('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
506
+ render('!!! transitional', :format => :html4).strip)
507
+ assert_equal('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
508
+ render('!!!', :format => :html4).strip)
509
+ end
510
+
511
+ def test_attr_wrapper
512
+ assert_equal("<p strange=*attrs*></p>\n", render("%p{ :strange => 'attrs'}", :attr_wrapper => '*'))
513
+ assert_equal("<p escaped='quo\"te'></p>\n", render("%p{ :escaped => 'quo\"te'}", :attr_wrapper => '"'))
514
+ assert_equal("<p escaped=\"quo'te\"></p>\n", render("%p{ :escaped => 'quo\\'te'}", :attr_wrapper => '"'))
515
+ assert_equal("<p escaped=\"q'uo&quot;te\"></p>\n", render("%p{ :escaped => 'q\\'uo\"te'}", :attr_wrapper => '"'))
516
+ assert_equal("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n", render("!!! XML", :attr_wrapper => '"'))
517
+ end
518
+
519
+ def test_attrs_parsed_correctly
520
+ assert_equal("<p boom=>biddly='bar =&gt; baz'></p>\n", render("%p{'boom=>biddly' => 'bar => baz'}"))
521
+ assert_equal("<p foo,bar='baz, qux'></p>\n", render("%p{'foo,bar' => 'baz, qux'}"))
522
+ assert_equal("<p escaped='quo&#x000A;te'></p>\n", render("%p{ :escaped => \"quo\\nte\"}"))
523
+ assert_equal("<p escaped='quo4te'></p>\n", render("%p{ :escaped => \"quo\#{2 + 2}te\"}"))
524
+ end
525
+
526
+ def test_correct_parsing_with_brackets
527
+ assert_equal("<p class='foo'>{tada} foo</p>\n", render("%p{:class => 'foo'} {tada} foo"))
528
+ assert_equal("<p class='foo'>deep {nested { things }}</p>\n", render("%p{:class => 'foo'} deep {nested { things }}"))
529
+ assert_equal("<p class='bar foo'>{a { d</p>\n", render("%p{{:class => 'foo'}, :class => 'bar'} {a { d"))
530
+ assert_equal("<p foo='bar'>a}</p>\n", render("%p{:foo => 'bar'} a}"))
531
+
532
+ foo = []
533
+ foo[0] = Struct.new('Foo', :id).new
534
+ assert_equal("<p class='struct_foo' id='struct_foo_new'>New User]</p>\n",
535
+ render("%p[foo[0]] New User]", :locals => {:foo => foo}))
536
+ assert_equal("<p class='prefix_struct_foo' id='prefix_struct_foo_new'>New User]</p>\n",
537
+ render("%p[foo[0], :prefix] New User]", :locals => {:foo => foo}))
538
+
539
+ foo[0].id = 1
540
+ assert_equal("<p class='struct_foo' id='struct_foo_1'>New User]</p>\n",
541
+ render("%p[foo[0]] New User]", :locals => {:foo => foo}))
542
+ assert_equal("<p class='prefix_struct_foo' id='prefix_struct_foo_1'>New User]</p>\n",
543
+ render("%p[foo[0], :prefix] New User]", :locals => {:foo => foo}))
544
+ end
545
+
546
+ def test_empty_attrs
547
+ assert_equal("<p attr=''>empty</p>\n", render("%p{ :attr => '' } empty"))
548
+ assert_equal("<p attr=''>empty</p>\n", render("%p{ :attr => x } empty", :locals => {:x => ''}))
549
+ end
550
+
551
+ def test_nil_attrs
552
+ assert_equal("<p>nil</p>\n", render("%p{ :attr => nil } nil"))
553
+ assert_equal("<p>nil</p>\n", render("%p{ :attr => x } nil", :locals => {:x => nil}))
554
+ end
555
+
556
+ def test_nil_id_with_syntactic_id
557
+ assert_equal("<p id='foo'>nil</p>\n", render("%p#foo{:id => nil} nil"))
558
+ assert_equal("<p id='foo_bar'>nil</p>\n", render("%p#foo{{:id => 'bar'}, :id => nil} nil"))
559
+ assert_equal("<p id='foo_bar'>nil</p>\n", render("%p#foo{{:id => nil}, :id => 'bar'} nil"))
560
+ end
561
+
562
+ def test_nil_class_with_syntactic_class
563
+ assert_equal("<p class='foo'>nil</p>\n", render("%p.foo{:class => nil} nil"))
564
+ assert_equal("<p class='bar foo'>nil</p>\n", render("%p.bar.foo{:class => nil} nil"))
565
+ assert_equal("<p class='bar foo'>nil</p>\n", render("%p.foo{{:class => 'bar'}, :class => nil} nil"))
566
+ assert_equal("<p class='bar foo'>nil</p>\n", render("%p.foo{{:class => nil}, :class => 'bar'} nil"))
567
+ end
568
+
569
+ def test_locals
570
+ assert_equal("<p>Paragraph!</p>\n", render("%p= text", :locals => { :text => "Paragraph!" }))
571
+ end
572
+
573
+ def test_dynamic_attrs_shouldnt_register_as_literal_values
574
+ assert_equal("<p a='b2c'></p>\n", render('%p{:a => "b#{1 + 1}c"}'))
575
+ assert_equal("<p a='b2c'></p>\n", render("%p{:a => 'b' + (1 + 1).to_s + 'c'}"))
576
+ end
577
+
578
+ def test_dynamic_attrs_with_self_closed_tag
579
+ assert_equal("<a b='2' />\nc\n", render("%a{'b' => 1 + 1}/\n= 'c'\n"))
580
+ end
581
+
582
+ def test_exceptions
583
+ EXCEPTION_MAP.each do |key, value|
584
+ begin
585
+ render(key, :filename => "(exception test for #{key.inspect})")
586
+ rescue Exception => err
587
+ value = [value] unless value.is_a?(Array)
588
+ expected_message, line_no = value
589
+ line_no ||= key.split("\n").length
590
+ line_reported = err.backtrace[0].gsub(/\(.+\):/, '').to_i
591
+
592
+ assert_equal(expected_message, err.message, "Line: #{key}")
593
+ assert_equal(line_no, line_reported, "Line: #{key}")
594
+ else
595
+ assert(false, "Exception not raised for\n#{key}")
596
+ end
597
+ end
598
+ end
599
+
600
+ def test_exception_line
601
+ render("a\nb\n!!!\n c\nd")
602
+ rescue Haml::SyntaxError => e
603
+ assert_equal("(test_exception_line):4", e.backtrace[0])
604
+ else
605
+ assert(false, '"a\nb\n!!!\n c\nd" doesn\'t produce an exception')
606
+ end
607
+
608
+ def test_exception
609
+ render("%p\n hi\n %a= undefined\n= 12")
610
+ rescue Exception => e
611
+ assert_match("(test_exception):3", e.backtrace[0])
612
+ else
613
+ # Test failed... should have raised an exception
614
+ assert(false)
615
+ end
616
+
617
+ def test_compile_error
618
+ render("a\nb\n- fee)\nc")
619
+ rescue Exception => e
620
+ assert_match(/\(test_compile_error\):3: syntax error/i, e.message)
621
+ else
622
+ assert(false,
623
+ '"a\nb\n- fee)\nc" doesn\'t produce an exception!')
624
+ end
625
+
626
+ def test_unbalanced_brackets
627
+ render('foo #{1 + 5} foo #{6 + 7 bar #{8 + 9}')
628
+ rescue Haml::SyntaxError => e
629
+ assert_equal("Unbalanced brackets.", e.message)
630
+ end
631
+
632
+ def test_balanced_conditional_comments
633
+ assert_equal("<!--[if !(IE 6)|(IE 7)]> Bracket: ] <![endif]-->\n",
634
+ render("/[if !(IE 6)|(IE 7)] Bracket: ]"))
635
+ end
636
+
637
+ def test_empty_filter
638
+ assert_equal(<<END, render(':javascript'))
639
+ <script type='text/javascript'>
640
+ //<![CDATA[
641
+
642
+ //]]>
643
+ </script>
644
+ END
645
+ end
646
+
647
+ def test_ugly_filter
648
+ assert_equal(<<END, render(":sass\n #foo\n bar: baz", :ugly => true))
649
+ #foo {
650
+ bar: baz; }
651
+ END
652
+ end
653
+
654
+ def test_local_assigns_dont_modify_class
655
+ assert_equal("bar\n", render("= foo", :locals => {:foo => 'bar'}))
656
+ assert_equal(nil, defined?(foo))
657
+ end
658
+
659
+ def test_object_ref_with_nil_id
660
+ user = User.new
661
+ assert_equal("<p class='struct_user' id='struct_user_new'>New User</p>\n",
662
+ render("%p[user] New User", :locals => {:user => user}))
663
+ end
664
+
665
+ def test_object_ref_before_attrs
666
+ user = User.new 42
667
+ assert_equal("<p class='struct_user' id='struct_user_42' style='width: 100px;'>New User</p>\n",
668
+ render("%p[user]{:style => 'width: 100px;'} New User", :locals => {:user => user}))
669
+ end
670
+
671
+ def test_non_literal_attributes
672
+ assert_equal("<p a1='foo' a2='bar' a3='baz' />\n",
673
+ render("%p{a2, a1, :a3 => 'baz'}/",
674
+ :locals => {:a1 => {:a1 => 'foo'}, :a2 => {:a2 => 'bar'}}))
675
+ end
676
+
677
+ def test_render_should_accept_a_binding_as_scope
678
+ string = "This is a string!"
679
+ string.instance_variable_set("@var", "Instance variable")
680
+ b = string.instance_eval do
681
+ var = "Local variable"
682
+ binding
683
+ end
684
+
685
+ assert_equal("<p>THIS IS A STRING!</p>\n<p>Instance variable</p>\n<p>Local variable</p>\n",
686
+ render("%p= upcase\n%p= @var\n%p= var", :scope => b))
687
+ end
688
+
689
+ def test_yield_should_work_with_binding
690
+ assert_equal("12\nFOO\n", render("= yield\n= upcase", :scope => "foo".instance_eval{binding}) { 12 })
691
+ end
692
+
693
+ def test_yield_should_work_with_def_method
694
+ s = "foo"
695
+ engine("= yield\n= upcase").def_method(s, :render)
696
+ assert_equal("12\nFOO\n", s.render { 12 })
697
+ end
698
+
699
+ def test_def_method_with_module
700
+ engine("= yield\n= upcase").def_method(String, :render_haml)
701
+ assert_equal("12\nFOO\n", "foo".render_haml { 12 })
702
+ end
703
+
704
+ def test_def_method_locals
705
+ obj = Object.new
706
+ engine("%p= foo\n.bar{:baz => baz}= boom").def_method(obj, :render, :foo, :baz, :boom)
707
+ assert_equal("<p>1</p>\n<div baz='2' class='bar'>3</div>\n", obj.render(:foo => 1, :baz => 2, :boom => 3))
708
+ end
709
+
710
+ def test_render_proc_locals
711
+ proc = engine("%p= foo\n.bar{:baz => baz}= boom").render_proc(Object.new, :foo, :baz, :boom)
712
+ assert_equal("<p>1</p>\n<div baz='2' class='bar'>3</div>\n", proc[:foo => 1, :baz => 2, :boom => 3])
713
+ end
714
+
715
+ def test_render_proc_with_binding
716
+ assert_equal("FOO\n", engine("= upcase").render_proc("foo".instance_eval{binding}).call)
717
+ end
718
+
719
+ def test_ugly_true
720
+ assert_equal("<div id='outer'>\n<div id='inner'>\n<p>hello world</p>\n</div>\n</div>\n",
721
+ render("#outer\n #inner\n %p hello world", :ugly => true))
722
+
723
+ assert_equal("<p>#{'s' * 75}</p>\n",
724
+ render("%p #{'s' * 75}", :ugly => true))
725
+
726
+ assert_equal("<p>#{'s' * 75}</p>\n",
727
+ render("%p= 's' * 75", :ugly => true))
728
+ end
729
+
730
+ def test_auto_preserve_unless_ugly
731
+ assert_equal("<pre>foo&#x000A;bar</pre>\n", render('%pre="foo\nbar"'))
732
+ assert_equal("<pre>foo\nbar</pre>\n", render("%pre\n foo\n bar"))
733
+ assert_equal("<pre>foo\nbar</pre>\n", render('%pre="foo\nbar"', :ugly => true))
734
+ assert_equal("<pre>foo\nbar</pre>\n", render("%pre\n foo\n bar", :ugly => true))
735
+ end
736
+
737
+ def test_xhtml_output_option
738
+ assert_equal "<p>\n <br />\n</p>\n", render("%p\n %br", :format => :xhtml)
739
+ assert_equal "<a />\n", render("%a/", :format => :xhtml)
740
+ end
741
+
742
+ def test_arbitrary_output_option
743
+ assert_raise(Haml::Error, "Invalid output format :html1") { engine("%br", :format => :html1) }
744
+ end
745
+
746
+ # HTML 4.0
747
+
748
+ def test_html_has_no_self_closing_tags
749
+ assert_equal "<p>\n <br>\n</p>\n", render("%p\n %br", :format => :html4)
750
+ assert_equal "<br>\n", render("%br/", :format => :html4)
751
+ end
752
+
753
+ def test_html_renders_empty_node_with_closing_tag
754
+ assert_equal "<div class='foo'></div>\n", render(".foo", :format => :html4)
755
+ end
756
+
757
+ def test_html_doesnt_add_slash_to_self_closing_tags
758
+ assert_equal "<a>\n", render("%a/", :format => :html4)
759
+ assert_equal "<a foo='2'>\n", render("%a{:foo => 1 + 1}/", :format => :html4)
760
+ assert_equal "<meta>\n", render("%meta", :format => :html4)
761
+ assert_equal "<meta foo='2'>\n", render("%meta{:foo => 1 + 1}", :format => :html4)
762
+ end
763
+
764
+ def test_html_ignores_xml_prolog_declaration
765
+ assert_equal "", render('!!! XML', :format => :html4)
766
+ end
767
+
768
+ def test_html_has_different_doctype
769
+ assert_equal %{<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">\n},
770
+ render('!!!', :format => :html4)
771
+ end
772
+
773
+ # because anything before the doctype triggers quirks mode in IE
774
+ def test_xml_prolog_and_doctype_dont_result_in_a_leading_whitespace_in_html
775
+ assert_no_match(/^\s+/, render("!!! xml\n!!!", :format => :html4))
776
+ end
777
+
778
+ # HTML5
779
+ def test_html5_doctype
780
+ assert_equal %{<!DOCTYPE html>\n}, render('!!!', :format => :html5)
781
+ end
782
+ end