haml 4.0.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
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>