haml 4.0.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +1 -1
  3. data/CHANGELOG.md +117 -5
  4. data/FAQ.md +7 -17
  5. data/MIT-LICENSE +1 -1
  6. data/README.md +85 -42
  7. data/REFERENCE.md +181 -86
  8. data/Rakefile +47 -51
  9. data/lib/haml/attribute_builder.rb +163 -0
  10. data/lib/haml/attribute_compiler.rb +215 -0
  11. data/lib/haml/attribute_parser.rb +144 -0
  12. data/lib/haml/buffer.rb +38 -128
  13. data/lib/haml/compiler.rb +88 -295
  14. data/lib/haml/engine.rb +25 -41
  15. data/lib/haml/error.rb +3 -0
  16. data/lib/haml/escapable.rb +49 -0
  17. data/lib/haml/exec.rb +33 -19
  18. data/lib/haml/filters.rb +20 -24
  19. data/lib/haml/generator.rb +41 -0
  20. data/lib/haml/helpers/action_view_extensions.rb +3 -2
  21. data/lib/haml/helpers/action_view_mods.rb +44 -66
  22. data/lib/haml/helpers/action_view_xss_mods.rb +1 -0
  23. data/lib/haml/helpers/safe_erubi_template.rb +27 -0
  24. data/lib/haml/helpers/safe_erubis_template.rb +16 -4
  25. data/lib/haml/helpers/xss_mods.rb +18 -12
  26. data/lib/haml/helpers.rb +122 -58
  27. data/lib/haml/options.rb +39 -46
  28. data/lib/haml/parser.rb +278 -217
  29. data/lib/haml/{template/plugin.rb → plugin.rb} +8 -15
  30. data/lib/haml/railtie.rb +21 -11
  31. data/lib/haml/sass_rails_filter.rb +17 -4
  32. data/lib/haml/template/options.rb +12 -2
  33. data/lib/haml/template.rb +12 -6
  34. data/lib/haml/temple_engine.rb +120 -0
  35. data/lib/haml/temple_line_counter.rb +29 -0
  36. data/lib/haml/util.rb +80 -199
  37. data/lib/haml/version.rb +2 -1
  38. data/lib/haml.rb +2 -1
  39. data/test/attribute_parser_test.rb +101 -0
  40. data/test/engine_test.rb +306 -176
  41. data/test/filters_test.rb +32 -19
  42. data/test/gemfiles/Gemfile.rails-4.0.x +11 -0
  43. data/test/gemfiles/Gemfile.rails-4.0.x.lock +87 -0
  44. data/test/gemfiles/Gemfile.rails-4.1.x +5 -0
  45. data/test/gemfiles/Gemfile.rails-4.2.x +5 -0
  46. data/test/gemfiles/Gemfile.rails-5.0.x +4 -0
  47. data/test/helper_test.rb +282 -96
  48. data/test/options_test.rb +22 -0
  49. data/test/parser_test.rb +71 -4
  50. data/test/results/bemit.xhtml +4 -0
  51. data/test/results/eval_suppressed.xhtml +4 -4
  52. data/test/results/helpers.xhtml +43 -41
  53. data/test/results/helpful.xhtml +6 -3
  54. data/test/results/just_stuff.xhtml +21 -20
  55. data/test/results/list.xhtml +9 -9
  56. data/test/results/nuke_inner_whitespace.xhtml +22 -22
  57. data/test/results/nuke_outer_whitespace.xhtml +84 -92
  58. data/test/results/original_engine.xhtml +17 -17
  59. data/test/results/partial_layout.xhtml +4 -3
  60. data/test/results/partial_layout_erb.xhtml +4 -3
  61. data/test/results/partials.xhtml +11 -10
  62. data/test/results/silent_script.xhtml +63 -63
  63. data/test/results/standard.xhtml +156 -159
  64. data/test/results/tag_parsing.xhtml +19 -19
  65. data/test/results/very_basic.xhtml +2 -2
  66. data/test/results/whitespace_handling.xhtml +56 -50
  67. data/test/template_test.rb +44 -53
  68. data/test/template_test_helper.rb +38 -0
  69. data/test/templates/_text_area_helper.html.haml +4 -0
  70. data/test/templates/bemit.haml +3 -0
  71. data/test/templates/just_stuff.haml +1 -0
  72. data/test/templates/partial_layout_erb.erb +1 -1
  73. data/test/templates/standard_ugly.haml +1 -0
  74. data/test/templates/with_bom.haml +1 -0
  75. data/test/temple_line_counter_test.rb +40 -0
  76. data/test/test_helper.rb +26 -12
  77. data/test/util_test.rb +6 -47
  78. metadata +88 -106
  79. data/lib/haml/helpers/rails_323_textarea_fix.rb +0 -24
  80. data/test/gemfiles/Gemfile.rails-3.0.x +0 -5
  81. data/test/gemfiles/Gemfile.rails-3.1.x +0 -6
  82. data/test/gemfiles/Gemfile.rails-3.2.x +0 -5
  83. data/test/gemfiles/Gemfile.rails-master +0 -4
  84. data/test/templates/_av_partial_1_ugly.haml +0 -9
  85. data/test/templates/_av_partial_2_ugly.haml +0 -5
  86. data/test/templates/action_view_ugly.haml +0 -47
  87. data/test/templates/standard_ugly.haml +0 -43
data/test/helper_test.rb CHANGED
@@ -1,22 +1,13 @@
1
1
  require 'test_helper'
2
+ require "active_model/naming"
2
3
 
3
- class ActionView::Base
4
- def nested_tag
5
- content_tag(:span) {content_tag(:div) {"something"}}
6
- end
7
-
8
- def wacky_form
9
- form_tag("/foo") {"bar"}
10
- end
4
+ class FormModel
5
+ extend ActiveModel::Naming
11
6
  end
12
7
 
13
- module Haml::Helpers
14
- def something_that_uses_haml_concat
15
- haml_concat('foo').to_s
16
- end
17
- end
8
+ class HelperTest < Haml::TestCase
9
+ TEXT_AREA_CONTENT_REGEX = /<(textarea)[^>]*>\n(.*?)<\/\1>/im
18
10
 
19
- class HelperTest < MiniTest::Unit::TestCase
20
11
  Post = Struct.new('Post', :body, :error_field, :errors)
21
12
  class PostErrors
22
13
  def on(name)
@@ -31,15 +22,18 @@ class HelperTest < MiniTest::Unit::TestCase
31
22
  end
32
23
 
33
24
  def setup
34
- @base = ActionView::Base.new
35
- @base.controller = ActionController::Base.new
36
-
37
- if defined?(ActionController::Response)
38
- # This is needed for >=3.0.0
39
- @base.controller.response = ActionController::Response.new
40
- end
25
+ @base = Class.new(ActionView::Base) {
26
+ def nested_tag
27
+ content_tag(:span) {content_tag(:div) {"something"}}
28
+ end
41
29
 
42
- @base.instance_variable_set('@post', Post.new("Foo bar\nbaz", nil, PostErrors.new))
30
+ def wacky_form
31
+ form_tag("/foo") {"bar"}
32
+ end
33
+ }.new
34
+ @base.controller = ActionController::Base.new
35
+ @base.view_paths << File.expand_path("../templates", __FILE__)
36
+ @base.instance_variable_set(:@post, Post.new("Foo bar\nbaz", nil, PostErrors.new))
43
37
  end
44
38
 
45
39
  def render(text, options = {})
@@ -47,6 +41,32 @@ class HelperTest < MiniTest::Unit::TestCase
47
41
  super
48
42
  end
49
43
 
44
+ def test_rendering_with_escapes
45
+ def @base.render_something_with_haml_concat
46
+ haml_concat "<p>"
47
+ end
48
+ def @base.render_something_with_haml_tag_and_concat
49
+ haml_tag 'p' do
50
+ haml_concat '<foo>'
51
+ end
52
+ end
53
+
54
+ output = render(<<-HAML, :action_view)
55
+ - render_something_with_haml_concat
56
+ - render_something_with_haml_tag_and_concat
57
+ - render_something_with_haml_concat
58
+ HAML
59
+ assert_equal("&lt;p&gt;\n<p>\n &lt;foo&gt;\n</p>\n&lt;p&gt;\n", output)
60
+ end
61
+
62
+ def test_with_raw_haml_concat
63
+ haml = <<HAML
64
+ - with_raw_haml_concat do
65
+ - haml_concat "<>&"
66
+ HAML
67
+ assert_equal("<>&\n", render(haml, :action_view))
68
+ end
69
+
50
70
  def test_flatten
51
71
  assert_equal("FooBar", Haml::Helpers.flatten("FooBar"))
52
72
 
@@ -59,13 +79,13 @@ class HelperTest < MiniTest::Unit::TestCase
59
79
  end
60
80
 
61
81
  def test_list_of_should_render_correctly
62
- assert_equal("<li>1</li>\n<li>2</li>\n", render("= list_of([1, 2]) do |i|\n = i"))
63
- assert_equal("<li>[1]</li>\n", render("= list_of([[1]]) do |i|\n = i.inspect"))
64
- assert_equal("<li>\n <h1>Fee</h1>\n <p>A word!</p>\n</li>\n<li>\n <h1>Fi</h1>\n <p>A word!</p>\n</li>\n<li>\n <h1>Fo</h1>\n <p>A word!</p>\n</li>\n<li>\n <h1>Fum</h1>\n <p>A word!</p>\n</li>\n",
82
+ assert_equal("<li>1</li>\n<li>2</li>", render("= list_of([1, 2]) do |i|\n = i"))
83
+ assert_equal("<li>[1]</li>", render("= list_of([[1]]) do |i|\n = i.inspect"))
84
+ assert_equal("<li>\n <h1>Fee</h1>\n <p>A word!</p>\n</li>\n<li>\n <h1>Fi</h1>\n <p>A word!</p>\n</li>\n<li>\n <h1>Fo</h1>\n <p>A word!</p>\n</li>\n<li>\n <h1>Fum</h1>\n <p>A word!</p>\n</li>",
65
85
  render("= list_of(['Fee', 'Fi', 'Fo', 'Fum']) do |title|\n %h1= title\n %p A word!"))
66
- assert_equal("<li c='3'>1</li>\n<li c='3'>2</li>\n", render("= list_of([1, 2], {:c => 3}) do |i|\n = i"))
67
- assert_equal("<li c='3'>[1]</li>\n", render("= list_of([[1]], {:c => 3}) do |i|\n = i.inspect"))
68
- assert_equal("<li c='3'>\n <h1>Fee</h1>\n <p>A word!</p>\n</li>\n<li c='3'>\n <h1>Fi</h1>\n <p>A word!</p>\n</li>\n<li c='3'>\n <h1>Fo</h1>\n <p>A word!</p>\n</li>\n<li c='3'>\n <h1>Fum</h1>\n <p>A word!</p>\n</li>\n",
86
+ assert_equal("<li c='3'>1</li>\n<li c='3'>2</li>", render("= list_of([1, 2], {:c => 3}) do |i|\n = i"))
87
+ assert_equal("<li c='3'>[1]</li>", render("= list_of([[1]], {:c => 3}) do |i|\n = i.inspect"))
88
+ assert_equal("<li c='3'>\n <h1>Fee</h1>\n <p>A word!</p>\n</li>\n<li c='3'>\n <h1>Fi</h1>\n <p>A word!</p>\n</li>\n<li c='3'>\n <h1>Fo</h1>\n <p>A word!</p>\n</li>\n<li c='3'>\n <h1>Fum</h1>\n <p>A word!</p>\n</li>",
69
89
  render("= list_of(['Fee', 'Fi', 'Fo', 'Fum'], {:c => 3}) do |title|\n %h1= title\n %p A word!"))
70
90
  end
71
91
 
@@ -75,15 +95,15 @@ class HelperTest < MiniTest::Unit::TestCase
75
95
  end
76
96
 
77
97
  def test_tabs
78
- assert_equal("foo\n bar\nbaz\n", render("foo\n- tab_up\nbar\n- tab_down\nbaz"))
79
- assert_equal(" <p>tabbed</p>\n", render("- buffer.tabulation=5\n%p tabbed"))
98
+ assert_equal("foo\nbar\nbaz\n", render("foo\n- tab_up\nbar\n- tab_down\nbaz"))
99
+ assert_equal("<p>tabbed</p>\n", render("- buffer.tabulation=5\n%p tabbed"))
80
100
  end
81
101
 
82
102
  def test_with_tabs
83
103
  assert_equal(<<HTML, render(<<HAML))
84
104
  Foo
85
- Bar
86
- Baz
105
+ Bar
106
+ Baz
87
107
  Baz
88
108
  HTML
89
109
  Foo
@@ -100,7 +120,7 @@ HAML
100
120
 
101
121
  begin
102
122
  ActionView::Base.new.render(:inline => "<%= flatten('Foo\\nBar') %>")
103
- rescue NoMethodError, Haml::Util.av_template_class(:Error)
123
+ rescue NoMethodError, ActionView::Template::Error
104
124
  proper_behavior = true
105
125
  end
106
126
  assert(proper_behavior)
@@ -118,53 +138,67 @@ HAML
118
138
  end
119
139
 
120
140
  def test_form_tag
121
- # This is usually provided by ActionController::Base.
122
141
  def @base.protect_against_forgery?; false; end
123
- assert_equal(<<HTML, render(<<HAML, :action_view))
124
- <form accept-charset="UTF-8" action="foo" method="post">#{rails_form_opener}
125
- <p>bar</p>
126
- <strong>baz</strong>
127
- </form>
128
- HTML
142
+ rendered = render(<<HAML, :action_view)
129
143
  = form_tag 'foo' do
130
- %p bar
131
- %strong baz
144
+ %p bar
145
+ %strong baz
132
146
  HAML
147
+ fragment = Nokogiri::HTML.fragment(rendered)
148
+ assert_equal 'foo', fragment.css('form').first.attributes['action'].to_s
149
+ assert_equal 'bar', fragment.css('form p').first.text.strip
150
+ assert_equal 'baz', fragment.css('form strong').first.text.strip
133
151
  end
134
152
 
135
- if ActionPack::VERSION::MAJOR == 4
136
- def test_text_area
137
- assert_equal(%(<textarea id="body" name="body">\nFoo&#x000A;Bar&#x000A; Baz&#x000A; Boom</textarea>\n),
138
- render('= text_area_tag "body", "Foo\nBar\n Baz\n Boom"', :action_view))
153
+ def test_form_for
154
+ # FIXME: current HAML doesn't do proper indentation with form_for (it's the capture { output } in #form_for).
155
+ def @base.protect_against_forgery?; false; end
156
+ rendered = render(<<HAML, :action_view)
157
+ = form_for OpenStruct.new, url: 'foo', as: :post do |f|
158
+ = f.text_field :name
159
+ HAML
160
+ assert_match(/<(form|div)[^>]+><input/, rendered)
161
+ end
139
162
 
140
- assert_equal(%(<textarea id="post_body" name="post[body]">\nFoo bar&#x000A;baz</textarea>\n),
141
- render('= text_area :post, :body', :action_view))
163
+ def test_pre
164
+ assert_equal(%(<pre>Foo bar&#x000A; baz</pre>\n),
165
+ render('= content_tag "pre", "Foo bar\n baz"', :action_view))
166
+ end
142
167
 
143
- assert_equal(%(<pre>Foo bar&#x000A; baz</pre>\n),
144
- render('= content_tag "pre", "Foo bar\n baz"', :action_view))
145
- end
146
- elsif (ActionPack::VERSION::MAJOR == 3) && (ActionPack::VERSION::MINOR >= 2) && (ActionPack::VERSION::TINY >= 3)
147
- def test_text_area
148
- assert_equal(%(<textarea id="body" name="body">\nFoo&#x000A;Bar&#x000A; Baz&#x000A; Boom</textarea>\n),
149
- render('= text_area_tag "body", "Foo\nBar\n Baz\n Boom"', :action_view))
168
+ def test_text_area_tag
169
+ output = render('= text_area_tag "body", "Foo\nBar\n Baz\n Boom"', :action_view)
170
+ match_data = output.match(TEXT_AREA_CONTENT_REGEX)
171
+ assert_equal "Foo&#x000A;Bar&#x000A; Baz&#x000A; Boom", match_data[2]
172
+ end
150
173
 
151
- assert_equal(%(<textarea cols="40" id="post_body" name="post[body]" rows="20">\nFoo bar&#x000A;baz</textarea>\n),
152
- render('= text_area :post, :body', :action_view))
174
+ def test_text_area
175
+ output = render('= text_area :post, :body', :action_view)
176
+ match_data = output.match(TEXT_AREA_CONTENT_REGEX)
177
+ assert_equal "Foo bar&#x000A;baz", match_data[2]
178
+ end
153
179
 
154
- assert_equal(%(<pre>Foo bar&#x000A; baz</pre>\n),
155
- render('= content_tag "pre", "Foo bar\n baz"', :action_view))
156
- end
157
- else
158
- def test_text_area
159
- assert_equal(%(<textarea id="body" name="body">Foo&#x000A;Bar&#x000A; Baz&#x000A; Boom</textarea>\n),
160
- render('= text_area_tag "body", "Foo\nBar\n Baz\n Boom"', :action_view))
180
+ def test_partials_should_not_cause_textareas_to_be_indented
181
+ # non-indentation of textareas rendered inside partials
182
+ @base.instance_variable_set(:@post, Post.new("Foo", nil, PostErrors.new))
183
+ output = render(".foo\n .bar\n = render '/text_area_helper'", :action_view)
184
+ match_data = output.match(TEXT_AREA_CONTENT_REGEX)
185
+ assert_equal 'Foo', match_data[2]
186
+ end
161
187
 
162
- assert_equal(%(<textarea cols="40" id="post_body" name="post[body]" rows="20">Foo bar&#x000A;baz</textarea>\n),
163
- render('= text_area :post, :body', :action_view))
188
+ def test_textareas_should_preserve_leading_whitespace
189
+ # leading whitespace preservation
190
+ @base.instance_variable_set(:@post, Post.new(" Foo", nil, PostErrors.new))
191
+ output = render(".foo\n = text_area :post, :body", :action_view)
192
+ match_data = output.match(TEXT_AREA_CONTENT_REGEX)
193
+ assert_equal '&#x0020; Foo', match_data[2]
194
+ end
164
195
 
165
- assert_equal(%(<pre>Foo bar&#x000A; baz</pre>\n),
166
- render('= content_tag "pre", "Foo bar\n baz"', :action_view))
167
- end
196
+ def test_textareas_should_preserve_leading_whitespace_in_partials
197
+ # leading whitespace in textareas rendered inside partials
198
+ @base.instance_variable_set(:@post, Post.new(" Foo", nil, PostErrors.new))
199
+ output = render(".foo\n .bar\n = render '/text_area_helper'", :action_view)
200
+ match_data = output.match(TEXT_AREA_CONTENT_REGEX)
201
+ assert_equal '&#x0020; Foo', match_data[2]
168
202
  end
169
203
 
170
204
  def test_capture_haml
@@ -202,11 +236,10 @@ HAML
202
236
 
203
237
  def test_form_tag_in_helper_with_string_block
204
238
  def @base.protect_against_forgery?; false; end
205
- assert_equal(<<HTML, render(<<HAML, :action_view))
206
- <form accept-charset="UTF-8" action="/foo" method="post">#{rails_form_opener}bar</form>
207
- HTML
208
- = wacky_form
209
- HAML
239
+ rendered = render('= wacky_form', :action_view)
240
+ fragment = Nokogiri::HTML.fragment(rendered)
241
+ assert_equal 'bar', fragment.text.strip
242
+ assert_equal '/foo', fragment.css('form').first.attributes['action'].to_s
210
243
  end
211
244
 
212
245
  def test_haml_tag_name_attribute_with_id
@@ -265,8 +298,12 @@ HAML
265
298
  assert_equal("<p id='foo&amp;bar'>baz</p>\n", render("%p{:id => 'foo&bar'} baz", :escape_html => true))
266
299
  end
267
300
 
268
- def test_haml_tag_autoclosed_tags_are_closed
269
- assert_equal("<br class='foo' />\n", render("- haml_tag :br, :class => 'foo'"))
301
+ def test_haml_tag_autoclosed_tags_are_closed_xhtml
302
+ assert_equal("<br class='foo' />\n", render("- haml_tag :br, :class => 'foo'", :format => :xhtml))
303
+ end
304
+
305
+ def test_haml_tag_autoclosed_tags_are_closed_html
306
+ assert_equal("<br class='foo'>\n", render("- haml_tag :br, :class => 'foo'", :format => :html5))
270
307
  end
271
308
 
272
309
  def test_haml_tag_with_class_array
@@ -297,7 +334,8 @@ HAML
297
334
  end
298
335
 
299
336
  def test_haml_tag_flags
300
- assert_equal("<p />\n", render("- haml_tag :p, :/"))
337
+ assert_equal("<p />\n", render("- haml_tag :p, :/", :format => :xhtml))
338
+ assert_equal("<p>\n", render("- haml_tag :p, :/", :format => :html5))
301
339
  assert_equal("<p>kumquat</p>\n", render("- haml_tag :p, :< do\n kumquat"))
302
340
 
303
341
  assert_raises(Haml::Error) { render("- haml_tag :p, 'foo', :/") }
@@ -320,12 +358,19 @@ HTML
320
358
  HAML
321
359
  end
322
360
 
361
+ def test_haml_concat_inside_haml_tag_escaped_with_xss
362
+ assert_equal("<p>\n &lt;&gt;&amp;\n</p>\n", render(<<HAML, :action_view))
363
+ - haml_tag :p do
364
+ - haml_concat "<>&"
365
+ HAML
366
+ end
367
+
323
368
  def test_haml_concat_with_multiline_string
324
369
  assert_equal(<<HTML, render(<<HAML))
325
370
  <p>
326
- foo
327
- bar
328
- baz
371
+ foo
372
+ bar
373
+ baz
329
374
  </p>
330
375
  HTML
331
376
  %p
@@ -333,10 +378,10 @@ HTML
333
378
  HAML
334
379
  end
335
380
 
336
- def test_haml_tag_with_ugly
337
- assert_equal(<<HTML, render(<<HAML, :ugly => true))
381
+ def test_haml_tag
382
+ assert_equal(<<HTML, render(<<HAML))
338
383
  <p>
339
- <strong>Hi!</strong>
384
+ <strong>Hi!</strong>
340
385
  </p>
341
386
  HTML
342
387
  - haml_tag :p do
@@ -344,6 +389,41 @@ HTML
344
389
  HAML
345
390
  end
346
391
 
392
+ def test_haml_tag_if_positive
393
+ assert_equal(<<HTML, render(<<HAML))
394
+ <div class='conditional'>
395
+ <p>A para</p>
396
+ </div>
397
+ HTML
398
+ - haml_tag_if true, '.conditional' do
399
+ %p A para
400
+ HAML
401
+ end
402
+
403
+ def test_haml_tag_if_positive_with_attributes
404
+ assert_equal(<<HTML, render(<<HAML))
405
+ <div class='conditional' foo='bar'>
406
+ <p>A para</p>
407
+ </div>
408
+ HTML
409
+ - haml_tag_if true, '.conditional', {:foo => 'bar'} do
410
+ %p A para
411
+ HAML
412
+ end
413
+
414
+ def test_haml_tag_if_negative
415
+ assert_equal(<<HTML, render(<<HAML))
416
+ <p>A para</p>
417
+ HTML
418
+ - haml_tag_if false, '.conditional' do
419
+ %p A para
420
+ HAML
421
+ end
422
+
423
+ def test_haml_tag_if_error_return
424
+ assert_raises(Haml::Error) { render("= haml_tag_if false, '.conditional' do\n %p Hello") }
425
+ end
426
+
347
427
  def test_is_haml
348
428
  assert(!ActionView::Base.new.is_haml?)
349
429
  assert_equal("true\n", render("= is_haml?"))
@@ -365,21 +445,72 @@ HAML
365
445
  end
366
446
 
367
447
  def test_capture_deals_properly_with_collections
368
- Haml::Helpers.module_eval do
369
- def trc(collection, &block)
370
- collection.each do |record|
371
- haml_concat capture_haml(record, &block)
372
- end
448
+ obj = Object.new
449
+ def obj.trc(collection, &block)
450
+ collection.each do |record|
451
+ haml_concat capture_haml(record, &block)
373
452
  end
374
453
  end
375
454
 
376
- assert_equal("1\n\n2\n\n3\n\n", render("- trc([1, 2, 3]) do |i|\n = i.inspect"))
455
+ assert_equal("1\n\n2\n\n3\n\n", render("- trc([1, 2, 3]) do |i|\n = i.inspect", scope: obj))
377
456
  end
378
457
 
379
458
  def test_capture_with_string_block
380
459
  assert_equal("foo\n", render("= capture { 'foo' }", :action_view))
381
460
  end
382
461
 
462
+ def test_capture_with_non_string_value_reurns_nil
463
+ def @base.check_capture_returns_nil(&block)
464
+ contents = capture(&block)
465
+
466
+ contents << "ERROR" if contents
467
+ end
468
+
469
+ assert_equal("\n", render("= check_capture_returns_nil { 2 }", :action_view))
470
+ end
471
+
472
+
473
+ class HomemadeViewContext
474
+ include ActionView::Context
475
+ include ActionView::Helpers::FormHelper
476
+
477
+ def initialize
478
+ _prepare_context
479
+ end
480
+
481
+ def url_for(*)
482
+ "/"
483
+ end
484
+
485
+ def dom_class(*)
486
+ end
487
+
488
+ def dom_id(*)
489
+ end
490
+
491
+ def m # I have to inject the model into the view using an instance method, using locals doesn't work.
492
+ FormModel.new
493
+ end
494
+
495
+ def protect_against_forgery?
496
+ end
497
+
498
+ # def capture(*args, &block)
499
+ # capture_haml(*args, &block)
500
+ # end
501
+ end
502
+
503
+ def test_form_for_with_homemade_view_context
504
+ handler = ActionView::Template.handler_for_extension("haml")
505
+ template = ActionView::Template.new(<<HAML, "inline template", handler, {})
506
+ = form_for(m, :url => "/") do
507
+ %b Bold!
508
+ HAML
509
+
510
+ # see if Bold is within form tags:
511
+ assert_match(/<form.*>.*<b>Bold!<\/b>.*<\/form>/m, template.render(HomemadeViewContext.new, {}))
512
+ end
513
+
383
514
  def test_find_and_preserve_with_block
384
515
  assert_equal("<pre>Foo&#x000A;Bar</pre>\nFoo\nBar\n",
385
516
  render("= find_and_preserve do\n %pre\n Foo\n Bar\n Foo\n Bar"))
@@ -391,7 +522,7 @@ HAML
391
522
  end
392
523
 
393
524
  def test_preserve_with_block
394
- assert_equal("<pre>Foo&#x000A;Bar</pre>&#x000A;Foo&#x000A;Bar\n",
525
+ assert_equal("<pre>Foo&#x000A;Bar</pre>&#x000A;Foo&#x000A;Bar",
395
526
  render("= preserve do\n %pre\n Foo\n Bar\n Foo\n Bar"))
396
527
  end
397
528
 
@@ -428,17 +559,22 @@ MESSAGE
428
559
  end
429
560
 
430
561
  def test_error_return_line
431
- render("%p foo\n= haml_concat 'foo'\n%p bar")
562
+ render("%p foo\n= haml_concat('foo').to_s\n%p bar")
432
563
  assert false, "Expected Haml::Error"
433
564
  rescue Haml::Error => e
434
- assert_equal 2, e.backtrace[1].scan(/:(\d+)/).first.first.to_i
565
+ assert_equal 2, e.backtrace[0].scan(/:(\d+)/).first.first.to_i
435
566
  end
436
567
 
437
568
  def test_error_return_line_in_helper
438
- render("- something_that_uses_haml_concat")
569
+ obj = Object.new
570
+ def obj.something_that_uses_haml_concat
571
+ haml_concat('foo').to_s
572
+ end
573
+
574
+ render("- something_that_uses_haml_concat", scope: obj)
439
575
  assert false, "Expected Haml::Error"
440
576
  rescue Haml::Error => e
441
- assert_equal 15, e.backtrace[0].scan(/:(\d+)/).first.first.to_i
577
+ assert_equal __LINE__ - 6, e.backtrace[0].scan(/:(\d+)/).first.first.to_i
442
578
  end
443
579
 
444
580
  class ActsLikeTag
@@ -474,6 +610,14 @@ HAML
474
610
  assert_equal "&quot;&gt;&lt;&amp;", Haml::Helpers.html_escape('"><&')
475
611
  end
476
612
 
613
+ def test_html_escape_should_work_on_frozen_strings
614
+ begin
615
+ assert Haml::Helpers.html_escape('foo'.freeze)
616
+ rescue => e
617
+ flunk e.message
618
+ end
619
+ end
620
+
477
621
  def test_html_escape_encoding
478
622
  old_stderr, $stderr = $stderr, StringIO.new
479
623
  string = "\"><&\u00e9" # if you're curious, u00e9 is "LATIN SMALL LETTER E WITH ACUTE"
@@ -483,6 +627,11 @@ HAML
483
627
  $stderr = old_stderr
484
628
  end
485
629
 
630
+ def test_html_escape_non_string
631
+ assert_equal('4.58', Haml::Helpers.html_escape(4.58))
632
+ assert_equal('4.58', Haml::Helpers.html_escape_without_haml_xss(4.58))
633
+ end
634
+
486
635
  def test_escape_once
487
636
  assert_equal "&quot;&gt;&lt;&amp;", Haml::Helpers.escape_once('"><&')
488
637
  end
@@ -493,7 +642,7 @@ HAML
493
642
 
494
643
  def test_escape_once_leaves_numeric_references
495
644
  assert_equal "&quot;&gt;&lt;&amp; &#160;", Haml::Helpers.escape_once('"><& &#160;') #decimal
496
- #assert_equal "&quot;&gt;&lt;&amp; &#x00a0;", Haml::Helpers.escape_once('"><& &#x00a0;') #hexadecimal
645
+ assert_equal "&quot;&gt;&lt;&amp; &#x00a0;", Haml::Helpers.escape_once('"><& &#x00a0;') #hexadecimal
497
646
  end
498
647
 
499
648
  def test_escape_once_encoding
@@ -505,5 +654,42 @@ HAML
505
654
  $stderr = old_stderr
506
655
  end
507
656
 
508
- end
657
+ def test_html_attrs_xhtml
658
+ assert_equal("<html lang='en-US' xml:lang='en-US' xmlns='http://www.w3.org/1999/xhtml'></html>\n",
659
+ render("%html{html_attrs}", :format => :xhtml))
660
+ end
509
661
 
662
+ def test_html_attrs_html4
663
+ assert_equal("<html lang='en-US'></html>\n",
664
+ render("%html{html_attrs}", :format => :html4))
665
+ end
666
+
667
+ def test_html_attrs_html5
668
+ assert_equal("<html lang='en-US'></html>\n",
669
+ render("%html{html_attrs}", :format => :html5))
670
+ end
671
+
672
+ def test_html_attrs_xhtml_other_lang
673
+ assert_equal("<html lang='es-AR' xml:lang='es-AR' xmlns='http://www.w3.org/1999/xhtml'></html>\n",
674
+ render("%html{html_attrs('es-AR')}", :format => :xhtml))
675
+ end
676
+
677
+ def test_html_attrs_html4_other_lang
678
+ assert_equal("<html lang='es-AR'></html>\n",
679
+ render("%html{html_attrs('es-AR')}", :format => :html4))
680
+ end
681
+
682
+ def test_html_attrs_html5_other_lang
683
+ assert_equal("<html lang='es-AR'></html>\n",
684
+ render("%html{html_attrs('es-AR')}", :format => :html5))
685
+ end
686
+
687
+ def test_escape_once_should_work_on_frozen_strings
688
+ begin
689
+ Haml::Helpers.escape_once('foo'.freeze)
690
+ rescue => e
691
+ flunk e.message
692
+ end
693
+ end
694
+
695
+ end
@@ -0,0 +1,22 @@
1
+ require 'test_helper'
2
+
3
+ module Haml
4
+ class OptionsTest < Haml::TestCase
5
+ def test_buffer_defaults_have_only_buffer_option_keys
6
+ assert_equal(
7
+ Haml::Options.buffer_option_keys.sort,
8
+ Haml::Options.buffer_defaults.keys.sort,
9
+ )
10
+ end
11
+
12
+ def test_buffer_defaults_values_are_the_same_as_rails_defaults
13
+ rails_defaults = Haml::Options.defaults.merge(Haml::Template.options)
14
+ Haml::Options.buffer_option_keys.each do |key|
15
+ assert_equal(
16
+ rails_defaults[key],
17
+ Haml::Options.buffer_defaults[key], "key: #{key}"
18
+ )
19
+ end
20
+ end
21
+ end
22
+ end
data/test/parser_test.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'test_helper'
2
2
 
3
3
  module Haml
4
- class ParserTest < MiniTest::Unit::TestCase
4
+ class ParserTest < Haml::TestCase
5
5
 
6
6
  test "should raise error for 'else' at wrong indent level" do
7
7
  begin
@@ -94,12 +94,79 @@ module Haml
94
94
  end
95
95
  end
96
96
 
97
+ test "revealed conditional comments are detected" do
98
+ text = "some revealed text"
99
+ cond = "[cond]"
100
+
101
+ node = parse("/!#{cond} #{text}").children[0]
102
+
103
+ assert_equal text, node.value[:text]
104
+ assert_equal cond, node.value[:conditional]
105
+ assert node.value[:revealed]
106
+ end
107
+
108
+ test "hidden conditional comments are detected" do
109
+ text = "some revealed text"
110
+ cond = "[cond]"
111
+
112
+ node = parse("/#{cond} #{text}").children[0]
113
+
114
+ assert_equal text, node.value[:text]
115
+ assert_equal cond, node.value[:conditional]
116
+ refute node.value[:revealed]
117
+ end
118
+
119
+ test "only script lines are checked for continuation keywords" do
120
+ haml = "- if true\n setup\n- else\n else\n"
121
+ node = parse(haml).children[0]
122
+ assert_equal(3, node.children.size)
123
+ end
124
+
125
+ # see #830. Strictly speaking the pipe here is not necessary, but there
126
+ # shouldn't be an error if it is there.
127
+ test "multiline Ruby with extra trailing pipe doesn't raise error" do
128
+ haml = "%p= foo bar, |\n baz"
129
+ begin
130
+ parse haml
131
+ rescue Haml::SyntaxError
132
+ flunk "Should not have raised SyntaxError"
133
+ end
134
+ end
135
+
136
+ test "empty filter doesn't hide following lines" do
137
+ root = parse "%p\n :plain\n %p\n"
138
+ p_element = root.children[0]
139
+ assert_equal 2, p_element.children.size
140
+ assert_equal :filter, p_element.children[0].type
141
+ assert_equal :tag, p_element.children[1].type
142
+ end
143
+
144
+ # Previously blocks under a haml_comment would be rejected if any line was
145
+ # indented by a value that wasn't a multiple of the document indentation.
146
+ test "haml_comment accepts any indentation in content" do
147
+ begin
148
+ parse "-\#\n Indented two spaces\n Indented three spaces"
149
+ rescue Haml::SyntaxError
150
+ flunk "haml_comment should accept any combination of indentation"
151
+ end
152
+ end
153
+
154
+ test "block haml_comment includes text" do
155
+ root = parse "-#\n Hello\n Hello\n"
156
+ assert_equal "Hello\n Hello\n", root.children[0].value[:text]
157
+ end
158
+
159
+ test "block haml_comment includes first line if present" do
160
+ root = parse "-# First line\n Hello\n Hello\n"
161
+ assert_equal " First line\nHello\n Hello\n", root.children[0].value[:text]
162
+ end
163
+
97
164
  private
98
165
 
99
166
  def parse(haml, options = nil)
100
167
  options ||= Options.new
101
- parser = Parser.new(haml, options)
102
- parser.parse
168
+ parser = Parser.new(options)
169
+ parser.call(haml)
103
170
  end
104
171
  end
105
- end
172
+ end
@@ -0,0 +1,4 @@
1
+ <div class='o-media@md c-user c-user--premium'>
2
+ <img alt='' class='o-media__img@md c-user__photo c-avatar' src='' />
3
+ <p class='o-media__body@md c-user__bio'>...</p>
4
+ </div>