haml-edge 2.1.1

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