jwhitmire-haml 2.1.0.1

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