haml-edge 2.1.1

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 (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