actionpack 2.3.4 → 2.3.5

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (74) hide show
  1. data/CHANGELOG +12 -0
  2. data/Rakefile +1 -1
  3. data/lib/action_controller.rb +3 -1
  4. data/lib/action_controller/assertions/dom_assertions.rb +19 -3
  5. data/lib/action_controller/assertions/selector_assertions.rb +16 -10
  6. data/lib/action_controller/base.rb +1 -1
  7. data/lib/action_controller/caching.rb +1 -0
  8. data/lib/action_controller/cookies.rb +2 -1
  9. data/lib/action_controller/http_authentication.rb +3 -2
  10. data/lib/action_controller/integration.rb +15 -3
  11. data/lib/action_controller/layout.rb +6 -1
  12. data/lib/action_controller/middlewares.rb +2 -0
  13. data/lib/action_controller/polymorphic_routes.rb +6 -21
  14. data/lib/action_controller/rack_lint_patch.rb +36 -0
  15. data/lib/action_controller/request_forgery_protection.rb +6 -2
  16. data/lib/action_controller/response.rb +2 -1
  17. data/lib/action_controller/string_coercion.rb +29 -0
  18. data/lib/action_controller/test_case.rb +6 -1
  19. data/lib/action_controller/test_process.rb +1 -1
  20. data/lib/action_controller/translation.rb +2 -2
  21. data/lib/action_controller/uploaded_file.rb +2 -2
  22. data/lib/action_controller/vendor/html-scanner/html/node.rb +1 -1
  23. data/lib/action_pack/version.rb +1 -1
  24. data/lib/action_view.rb +3 -3
  25. data/lib/action_view/base.rb +6 -1
  26. data/lib/action_view/erb/util.rb +6 -0
  27. data/lib/action_view/helpers.rb +2 -0
  28. data/lib/action_view/helpers/active_record_helper.rb +3 -3
  29. data/lib/action_view/helpers/asset_tag_helper.rb +2 -2
  30. data/lib/action_view/helpers/capture_helper.rb +2 -2
  31. data/lib/action_view/helpers/date_helper.rb +27 -15
  32. data/lib/action_view/helpers/form_helper.rb +32 -11
  33. data/lib/action_view/helpers/form_options_helper.rb +1 -1
  34. data/lib/action_view/helpers/form_tag_helper.rb +3 -3
  35. data/lib/action_view/helpers/number_helper.rb +6 -1
  36. data/lib/action_view/helpers/prototype_helper.rb +1 -1
  37. data/lib/action_view/helpers/raw_output_helper.rb +9 -0
  38. data/lib/action_view/helpers/sanitize_helper.rb +10 -2
  39. data/lib/action_view/helpers/tag_helper.rb +4 -4
  40. data/lib/action_view/helpers/translation_helper.rb +1 -1
  41. data/lib/action_view/helpers/url_helper.rb +3 -3
  42. data/lib/action_view/locale/en.yml +3 -0
  43. data/lib/action_view/partials.rb +1 -1
  44. data/lib/action_view/safe_buffer.rb +28 -0
  45. data/lib/action_view/template.rb +8 -2
  46. data/lib/action_view/test_case.rb +102 -27
  47. data/lib/actionpack.rb +1 -0
  48. data/test/controller/cookie_test.rb +7 -0
  49. data/test/controller/dom_assertions_test.rb +53 -0
  50. data/test/controller/filter_params_test.rb +1 -0
  51. data/test/controller/html-scanner/sanitizer_test.rb +1 -0
  52. data/test/controller/http_digest_authentication_test.rb +22 -0
  53. data/test/controller/integration_test.rb +38 -0
  54. data/test/controller/layout_test.rb +11 -0
  55. data/test/controller/polymorphic_routes_test.rb +4 -0
  56. data/test/controller/request_forgery_protection_test.rb +19 -1
  57. data/test/controller/routing_test.rb +23 -15
  58. data/test/controller/session/test_session_test.rb +2 -2
  59. data/test/fixtures/layout_tests/abs_path_layout.rhtml +1 -0
  60. data/test/fixtures/test/_from_helper.erb +1 -0
  61. data/test/template/active_record_helper_test.rb +1 -1
  62. data/test/template/asset_tag_helper_test.rb +12 -0
  63. data/test/template/benchmark_helper_test.rb +6 -6
  64. data/test/template/compiled_templates_test.rb +2 -1
  65. data/test/template/date_helper_i18n_test.rb +10 -9
  66. data/test/template/date_helper_test.rb +29 -13
  67. data/test/template/form_helper_test.rb +90 -10
  68. data/test/template/number_helper_test.rb +4 -0
  69. data/test/template/raw_output_helper_test.rb +21 -0
  70. data/test/template/sanitize_helper_test.rb +10 -1
  71. data/test/template/tag_helper_test.rb +1 -0
  72. data/test/view/safe_buffer_test.rb +36 -0
  73. data/test/view/test_case_test.rb +173 -5
  74. metadata +13 -4
@@ -207,6 +207,24 @@ class IntegrationTestTest < Test::Unit::TestCase
207
207
  assert_equal ::ActionController::Integration::Session, session2.class
208
208
  assert_not_equal session1, session2
209
209
  end
210
+
211
+ # RSpec mixes Matchers (which has a #method_missing) into
212
+ # IntegrationTest's superclass. Make sure IntegrationTest does not
213
+ # try to delegate these methods to the session object.
214
+ def test_does_not_prevent_method_missing_passing_up_to_ancestors
215
+ mixin = Module.new do
216
+ def method_missing(name, *args)
217
+ name.to_s == 'foo' ? 'pass' : super
218
+ end
219
+ end
220
+ @test.class.superclass.__send__(:include, mixin)
221
+ begin
222
+ assert_equal 'pass', @test.foo
223
+ ensure
224
+ # leave other tests as unaffected as possible
225
+ mixin.__send__(:remove_method, :method_missing)
226
+ end
227
+ end
210
228
  end
211
229
 
212
230
  # Tests that integration tests don't call Controller test methods for processing.
@@ -443,3 +461,23 @@ class MetalTest < ActionController::IntegrationTest
443
461
  assert_equal '', response.body
444
462
  end
445
463
  end
464
+
465
+ class StringSubclassBodyTest < ActionController::IntegrationTest
466
+ class SafeString < String
467
+ end
468
+
469
+ class SafeStringMiddleware
470
+ def self.call(env)
471
+ [200, {"Content-Type" => "text/plain", "Content-Length" => "12"}, [SafeString.new("Hello World!")]]
472
+ end
473
+ end
474
+
475
+ def setup
476
+ @integration_session = ActionController::Integration::Session.new(SafeStringMiddleware)
477
+ end
478
+
479
+ def test_string_subclass_body
480
+ get '/'
481
+ assert_equal 'Hello World!', response.body
482
+ end
483
+ end
@@ -83,6 +83,11 @@ class AbsolutePathLayoutController < LayoutTest
83
83
  layout File.expand_path(File.expand_path(__FILE__) + '/../../fixtures/layout_tests/layouts/layout_test.rhtml')
84
84
  end
85
85
 
86
+ class AbsolutePathWithoutLayoutsController < LayoutTest
87
+ # Absolute layout path without 'layouts' in it.
88
+ layout File.expand_path(File.expand_path(__FILE__) + '/../../fixtures/layout_tests/abs_path_layout.rhtml')
89
+ end
90
+
86
91
  class HasOwnLayoutController < LayoutTest
87
92
  layout 'item'
88
93
  end
@@ -153,6 +158,12 @@ class LayoutSetInResponseTest < ActionController::TestCase
153
158
  get :hello
154
159
  assert_equal "layout_test.rhtml hello.rhtml", @response.body.strip
155
160
  end
161
+
162
+ def test_absolute_pathed_layout_without_layouts_in_path
163
+ @controller = AbsolutePathWithoutLayoutsController.new
164
+ get :hello
165
+ assert_equal "abs_path_layout.rhtml hello.rhtml", @response.body.strip
166
+ end
156
167
  end
157
168
 
158
169
  class RenderWithTemplateOptionController < LayoutTest
@@ -290,4 +290,8 @@ class PolymorphicRoutesTest < ActiveSupport::TestCase
290
290
  polymorphic_url([:taxes])
291
291
  end
292
292
 
293
+ def test_with_array_containing_symbols
294
+ expects(:new_article_url).with()
295
+ polymorphic_url([:new, :article])
296
+ end
293
297
  end
@@ -22,7 +22,7 @@ module RequestForgeryProtectionActions
22
22
  def unsafe
23
23
  render :text => 'pwn'
24
24
  end
25
-
25
+
26
26
  def rescue_action(e) raise e end
27
27
  end
28
28
 
@@ -44,6 +44,13 @@ class FreeCookieController < RequestForgeryProtectionController
44
44
  end
45
45
  end
46
46
 
47
+ class CustomAuthenticityParamController < RequestForgeryProtectionController
48
+ def form_authenticity_param
49
+ 'foobar'
50
+ end
51
+ end
52
+
53
+
47
54
  # common test methods
48
55
 
49
56
  module RequestForgeryProtectionTests
@@ -245,3 +252,14 @@ class FreeCookieControllerTest < ActionController::TestCase
245
252
  end
246
253
  end
247
254
  end
255
+
256
+ class CustomAuthenticityParamControllerTest < ActionController::TestCase
257
+ def setup
258
+ ActionController::Base.request_forgery_protection_token = :authenticity_token
259
+ end
260
+
261
+ def test_should_allow_custom_token
262
+ post :index, :authenticity_token => 'foobar'
263
+ assert_response :ok
264
+ end
265
+ end
@@ -107,7 +107,11 @@ class StaticSegmentTest < Test::Unit::TestCase
107
107
  end
108
108
  end
109
109
 
110
- class DynamicSegmentTest < Test::Unit::TestCase
110
+ class DynamicSegmentTest < ActiveSupport::TestCase
111
+ def setup
112
+ @segment = nil
113
+ end
114
+
111
115
  def segment(options = {})
112
116
  unless @segment
113
117
  @segment = ROUTING::DynamicSegment.new(:a, options)
@@ -341,7 +345,11 @@ class ControllerSegmentTest < Test::Unit::TestCase
341
345
  end
342
346
  end
343
347
 
344
- class PathSegmentTest < Test::Unit::TestCase
348
+ class PathSegmentTest < ActiveSupport::TestCase
349
+ def setup
350
+ @segment = nil
351
+ end
352
+
345
353
  def segment(options = {})
346
354
  unless @segment
347
355
  @segment = ROUTING::PathSegment.new(:path, options)
@@ -754,7 +762,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
754
762
 
755
763
  ActionController::Routing.use_controllers! %w(content admin/user admin/news_feed)
756
764
  end
757
-
765
+
758
766
  def teardown
759
767
  @rs.clear!
760
768
  end
@@ -1094,21 +1102,21 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
1094
1102
  map.post 'post/:id', :controller=> 'post', :action=> 'show', :requirements => {:id => /\d+/}
1095
1103
  end
1096
1104
  exception = assert_raise(ActionController::RoutingError) { rs.generate(:controller => 'post', :action => 'show', :bad_param => "foo", :use_route => "post") }
1097
- assert_match /^post_url failed to generate/, exception.message
1105
+ assert_match(/^post_url failed to generate/, exception.message)
1098
1106
  from_match = exception.message.match(/from \{[^\}]+\}/).to_s
1099
- assert_match /:bad_param=>"foo"/, from_match
1100
- assert_match /:action=>"show"/, from_match
1101
- assert_match /:controller=>"post"/, from_match
1107
+ assert_match(/:bad_param=>"foo"/, from_match)
1108
+ assert_match(/:action=>"show"/, from_match)
1109
+ assert_match(/:controller=>"post"/, from_match)
1102
1110
 
1103
1111
  expected_match = exception.message.match(/expected: \{[^\}]+\}/).to_s
1104
- assert_no_match /:bad_param=>"foo"/, expected_match
1105
- assert_match /:action=>"show"/, expected_match
1106
- assert_match /:controller=>"post"/, expected_match
1112
+ assert_no_match(/:bad_param=>"foo"/, expected_match)
1113
+ assert_match( /:action=>"show"/, expected_match)
1114
+ assert_match( /:controller=>"post"/, expected_match)
1107
1115
 
1108
1116
  diff_match = exception.message.match(/diff: \{[^\}]+\}/).to_s
1109
- assert_match /:bad_param=>"foo"/, diff_match
1110
- assert_no_match /:action=>"show"/, diff_match
1111
- assert_no_match /:controller=>"post"/, diff_match
1117
+ assert_match( /:bad_param=>"foo"/, diff_match)
1118
+ assert_no_match(/:action=>"show"/, diff_match)
1119
+ assert_no_match(/:controller=>"post"/, diff_match)
1112
1120
  end
1113
1121
 
1114
1122
  # this specifies the case where your formerly would get a very confusing error message with an empty diff
@@ -2564,10 +2572,10 @@ class RouteLoadingTest < Test::Unit::TestCase
2564
2572
 
2565
2573
  routes.reload
2566
2574
  end
2567
-
2575
+
2568
2576
  def test_load_multiple_configurations
2569
2577
  routes.add_configuration_file("engines.rb")
2570
-
2578
+
2571
2579
  File.expects(:stat).at_least_once.returns(@stat)
2572
2580
 
2573
2581
  routes.expects(:load).with('./config/routes.rb')
@@ -33,11 +33,11 @@ class ActionController::TestSessionTest < ActiveSupport::TestCase
33
33
  assert_equal('value', session[:key])
34
34
  end
35
35
 
36
- def test_calling_delete_removes_item
36
+ def test_calling_delete_removes_item_and_returns_its_value
37
37
  session = ActionController::TestSession.new
38
38
  session[:key] = 'value'
39
39
  assert_equal('value', session[:key])
40
- session.delete(:key)
40
+ assert_equal('value', session.delete(:key))
41
41
  assert_nil(session[:key])
42
42
  end
43
43
 
@@ -0,0 +1 @@
1
+ abs_path_layout.rhtml <%= yield %>
@@ -0,0 +1 @@
1
+ <%= render_from_helper %>
@@ -183,7 +183,7 @@ class ActiveRecordHelperTest < ActionView::TestCase
183
183
  end
184
184
 
185
185
  def test_form_with_action_option
186
- @response.body = form("post", :action => "sign")
186
+ output_buffer << form("post", :action => "sign")
187
187
  assert_select "form[action=sign]" do |form|
188
188
  assert_select "input[type=submit][value=Sign]"
189
189
  end
@@ -164,6 +164,11 @@ class AssetTagHelperTest < ActionView::TestCase
164
164
  assert_dom_equal(%(<script src="/javascripts/prototype.js?1" type="text/javascript"></script>\n<script src="/javascripts/effects.js?1" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js?1" type="text/javascript"></script>\n<script src="/javascripts/controls.js?1" type="text/javascript"></script>\n<script src="/javascripts/application.js?1" type="text/javascript"></script>), javascript_include_tag(:defaults))
165
165
  end
166
166
 
167
+ def test_javascript_include_tag_is_html_safe
168
+ assert javascript_include_tag(:defaults).html_safe?
169
+ assert javascript_include_tag("prototype").html_safe?
170
+ end
171
+
167
172
  def test_register_javascript_include_default
168
173
  ENV["RAILS_ASSET_ID"] = ""
169
174
  ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'slider'
@@ -206,6 +211,13 @@ class AssetTagHelperTest < ActionView::TestCase
206
211
  StyleLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
207
212
  end
208
213
 
214
+ def test_stylesheet_link_tag_is_html_safe
215
+ ENV["RAILS_ASSET_ID"] = ""
216
+ assert stylesheet_link_tag('dir/file').html_safe?
217
+ assert stylesheet_link_tag('dir/other/file', 'dir/file2').html_safe?
218
+ assert stylesheet_tag('dir/file', {}).html_safe?
219
+ end
220
+
209
221
  def test_custom_stylesheet_expansions
210
222
  ActionView::Helpers::AssetTagHelper::register_stylesheet_expansion :monkey => ["head", "body", "tail"]
211
223
  assert_dom_equal %(<link href="/stylesheets/first.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/head.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/body.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/tail.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/last.css" media="screen" rel="stylesheet" type="text/css" />), stylesheet_link_tag('first', :monkey, 'last')
@@ -4,14 +4,14 @@ require 'action_view/helpers/benchmark_helper'
4
4
  class BenchmarkHelperTest < ActionView::TestCase
5
5
  tests ActionView::Helpers::BenchmarkHelper
6
6
 
7
- def teardown
8
- controller.logger.send(:clear_buffer)
7
+ def setup
8
+ super
9
+ controller.logger = ActiveSupport::BufferedLogger.new(StringIO.new)
10
+ controller.logger.auto_flushing = false
9
11
  end
10
12
 
11
- def controller
12
- logger = ActiveSupport::BufferedLogger.new(StringIO.new)
13
- logger.auto_flushing = false
14
- @controller ||= Struct.new(:logger).new(logger)
13
+ def teardown
14
+ controller.logger.send(:clear_buffer)
15
15
  end
16
16
 
17
17
  def test_without_block
@@ -2,8 +2,9 @@ require 'abstract_unit'
2
2
  require 'controller/fake_models'
3
3
 
4
4
  class CompiledTemplatesTest < Test::Unit::TestCase
5
-
5
+
6
6
  def setup
7
+ @explicit_view_paths = nil
7
8
  @compiled_templates = ActionView::Base::CompiledTemplates
8
9
  @compiled_templates.instance_methods.each do |m|
9
10
  @compiled_templates.send(:remove_method, m) if m =~ /^_run_/
@@ -20,15 +20,16 @@ class DateHelperDistanceOfTimeInWordsI18nTests < Test::Unit::TestCase
20
20
  [60.seconds, true] => [:'x_minutes', 1],
21
21
 
22
22
  # without include_seconds
23
- [29.seconds, false] => [:'less_than_x_minutes', 1],
24
- [60.seconds, false] => [:'x_minutes', 1],
25
- [44.minutes, false] => [:'x_minutes', 44],
26
- [61.minutes, false] => [:'about_x_hours', 1],
27
- [24.hours, false] => [:'x_days', 1],
28
- [30.days, false] => [:'about_x_months', 1],
29
- [60.days, false] => [:'x_months', 2],
30
- [1.year, false] => [:'about_x_years', 1],
31
- [3.years, false] => [:'over_x_years', 3]
23
+ [29.seconds, false] => [:'less_than_x_minutes', 1],
24
+ [60.seconds, false] => [:'x_minutes', 1],
25
+ [44.minutes, false] => [:'x_minutes', 44],
26
+ [61.minutes, false] => [:'about_x_hours', 1],
27
+ [24.hours, false] => [:'x_days', 1],
28
+ [30.days, false] => [:'about_x_months', 1],
29
+ [60.days, false] => [:'x_months', 2],
30
+ [1.year, false] => [:'about_x_years', 1],
31
+ [3.years + 6.months, false] => [:'over_x_years', 3],
32
+ [3.years + 10.months, false] => [:'almost_x_years', 4]
32
33
 
33
34
  }.each do |passed, expected|
34
35
  assert_distance_of_time_in_words_translates_key passed, expected
@@ -53,13 +53,14 @@ class DateHelperTest < ActionView::TestCase
53
53
  assert_equal "about 2 hours", distance_of_time_in_words(from, to + 89.minutes + 30.seconds)
54
54
  assert_equal "about 24 hours", distance_of_time_in_words(from, to + 23.hours + 59.minutes + 29.seconds)
55
55
 
56
- # 1440..2879
56
+ # 1440..2529
57
57
  assert_equal "1 day", distance_of_time_in_words(from, to + 23.hours + 59.minutes + 30.seconds)
58
- assert_equal "1 day", distance_of_time_in_words(from, to + 47.hours + 59.minutes + 29.seconds)
58
+ assert_equal "1 day", distance_of_time_in_words(from, to + 41.hours + 59.minutes + 29.seconds)
59
59
 
60
- # 2880..43199
61
- assert_equal "2 days", distance_of_time_in_words(from, to + 47.hours + 59.minutes + 30.seconds)
62
- assert_equal "29 days", distance_of_time_in_words(from, to + 29.days + 23.hours + 59.minutes + 29.seconds)
60
+ # 2530..43199
61
+ assert_equal "2 days", distance_of_time_in_words(from, to + 42.hours + 59.minutes + 30.seconds)
62
+ assert_equal "3 days", distance_of_time_in_words(from, to + 2.days + 12.hours)
63
+ assert_equal "30 days", distance_of_time_in_words(from, to + 29.days + 23.hours + 59.minutes + 29.seconds)
63
64
 
64
65
  # 43200..86399
65
66
  assert_equal "about 1 month", distance_of_time_in_words(from, to + 29.days + 23.hours + 59.minutes + 30.seconds)
@@ -69,13 +70,28 @@ class DateHelperTest < ActionView::TestCase
69
70
  assert_equal "2 months", distance_of_time_in_words(from, to + 59.days + 23.hours + 59.minutes + 30.seconds)
70
71
  assert_equal "12 months", distance_of_time_in_words(from, to + 1.years - 31.seconds)
71
72
 
72
- # 525600..1051199
73
- assert_equal "about 1 year", distance_of_time_in_words(from, to + 1.years - 30.seconds)
74
- assert_equal "about 1 year", distance_of_time_in_words(from, to + 2.years - 31.seconds)
75
-
76
- # > 1051199
77
- assert_equal "over 2 years", distance_of_time_in_words(from, to + 2.years + 30.seconds)
78
- assert_equal "over 10 years", distance_of_time_in_words(from, to + 10.years)
73
+ # > 525599
74
+ assert_equal "about 1 year", distance_of_time_in_words(from, to + 1.years - 30.seconds)
75
+ assert_equal "about 1 year", distance_of_time_in_words(from, to + 1.years + 3.months - 1.day)
76
+ assert_equal "over 1 year", distance_of_time_in_words(from, to + 1.years + 6.months)
77
+
78
+ assert_equal "almost 2 years", distance_of_time_in_words(from, to + 2.years - 3.months + 1.day)
79
+ assert_equal "about 2 years", distance_of_time_in_words(from, to + 2.years + 3.months - 1.day)
80
+ assert_equal "over 2 years", distance_of_time_in_words(from, to + 2.years + 3.months + 1.day)
81
+ assert_equal "over 2 years", distance_of_time_in_words(from, to + 2.years + 9.months - 1.day)
82
+ assert_equal "almost 3 years", distance_of_time_in_words(from, to + 2.years + 9.months + 1.day)
83
+
84
+ assert_equal "almost 5 years", distance_of_time_in_words(from, to + 5.years - 3.months + 1.day)
85
+ assert_equal "about 5 years", distance_of_time_in_words(from, to + 5.years + 3.months - 1.day)
86
+ assert_equal "over 5 years", distance_of_time_in_words(from, to + 5.years + 3.months + 1.day)
87
+ assert_equal "over 5 years", distance_of_time_in_words(from, to + 5.years + 9.months - 1.day)
88
+ assert_equal "almost 6 years", distance_of_time_in_words(from, to + 5.years + 9.months + 1.day)
89
+
90
+ assert_equal "almost 10 years", distance_of_time_in_words(from, to + 10.years - 3.months + 1.day)
91
+ assert_equal "about 10 years", distance_of_time_in_words(from, to + 10.years + 3.months - 1.day)
92
+ assert_equal "over 10 years", distance_of_time_in_words(from, to + 10.years + 3.months + 1.day)
93
+ assert_equal "over 10 years", distance_of_time_in_words(from, to + 10.years + 9.months - 1.day)
94
+ assert_equal "almost 11 years", distance_of_time_in_words(from, to + 10.years + 9.months + 1.day)
79
95
 
80
96
  # test to < from
81
97
  assert_equal "about 4 hours", distance_of_time_in_words(from + 4.hours, to)
@@ -104,7 +120,7 @@ class DateHelperTest < ActionView::TestCase
104
120
  def test_distance_in_words_with_dates
105
121
  start_date = Date.new 1975, 1, 31
106
122
  end_date = Date.new 1977, 1, 31
107
- assert_equal("over 2 years", distance_of_time_in_words(start_date, end_date))
123
+ assert_equal("about 2 years", distance_of_time_in_words(start_date, end_date))
108
124
  end
109
125
 
110
126
  def test_distance_in_words_with_integers
@@ -697,6 +697,26 @@ class FormHelperTest < ActionView::TestCase
697
697
  end
698
698
  end
699
699
 
700
+ expected = '<form action="http://www.example.com" method="post">' +
701
+ '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' +
702
+ '<input id="post_author_attributes_name" name="post[author_attributes][name]" size="30" type="text" value="author #321" />' +
703
+ '<input id="post_author_attributes_id" name="post[author_attributes][id]" type="hidden" value="321" />' +
704
+ '</form>'
705
+
706
+ assert_dom_equal expected, output_buffer
707
+ end
708
+
709
+ def test_nested_fields_for_with_existing_records_on_a_nested_attributes_one_to_one_association_with_explicit_hidden_field_placement
710
+ @post.author = Author.new(321)
711
+
712
+ form_for(:post, @post) do |f|
713
+ concat f.text_field(:title)
714
+ f.fields_for(:author) do |af|
715
+ concat af.hidden_field(:id)
716
+ concat af.text_field(:name)
717
+ end
718
+ end
719
+
700
720
  expected = '<form action="http://www.example.com" method="post">' +
701
721
  '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' +
702
722
  '<input id="post_author_attributes_id" name="post[author_attributes][id]" type="hidden" value="321" />' +
@@ -718,6 +738,30 @@ class FormHelperTest < ActionView::TestCase
718
738
  end
719
739
  end
720
740
 
741
+ expected = '<form action="http://www.example.com" method="post">' +
742
+ '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' +
743
+ '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #1" />' +
744
+ '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
745
+ '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="comment #2" />' +
746
+ '<input id="post_comments_attributes_1_id" name="post[comments_attributes][1][id]" type="hidden" value="2" />' +
747
+ '</form>'
748
+
749
+ assert_dom_equal expected, output_buffer
750
+ end
751
+
752
+ def test_nested_fields_for_with_existing_records_on_a_nested_attributes_collection_association_with_explicit_hidden_field_placement
753
+ @post.comments = Array.new(2) { |id| Comment.new(id + 1) }
754
+
755
+ form_for(:post, @post) do |f|
756
+ concat f.text_field(:title)
757
+ @post.comments.each do |comment|
758
+ f.fields_for(:comments, comment) do |cf|
759
+ concat cf.hidden_field(:id)
760
+ concat cf.text_field(:name)
761
+ end
762
+ end
763
+ end
764
+
721
765
  expected = '<form action="http://www.example.com" method="post">' +
722
766
  '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' +
723
767
  '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
@@ -764,14 +808,50 @@ class FormHelperTest < ActionView::TestCase
764
808
 
765
809
  expected = '<form action="http://www.example.com" method="post">' +
766
810
  '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' +
767
- '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="321" />' +
768
811
  '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #321" />' +
812
+ '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="321" />' +
769
813
  '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="new comment" />' +
770
814
  '</form>'
771
815
 
772
816
  assert_dom_equal expected, output_buffer
773
817
  end
774
818
 
819
+ def test_nested_fields_for_with_an_empty_supplied_attributes_collection
820
+ form_for(:post, @post) do |f|
821
+ concat f.text_field(:title)
822
+ f.fields_for(:comments, []) do |cf|
823
+ concat cf.text_field(:name)
824
+ end
825
+ end
826
+
827
+ expected = '<form action="http://www.example.com" method="post">' +
828
+ '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' +
829
+ '</form>'
830
+
831
+ assert_dom_equal expected, output_buffer
832
+ end
833
+
834
+ def test_nested_fields_for_with_existing_records_on_a_supplied_nested_attributes_collection
835
+ @post.comments = Array.new(2) { |id| Comment.new(id + 1) }
836
+
837
+ form_for(:post, @post) do |f|
838
+ concat f.text_field(:title)
839
+ f.fields_for(:comments, @post.comments) do |cf|
840
+ concat cf.text_field(:name)
841
+ end
842
+ end
843
+
844
+ expected = '<form action="http://www.example.com" method="post">' +
845
+ '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' +
846
+ '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #1" />' +
847
+ '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
848
+ '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="comment #2" />' +
849
+ '<input id="post_comments_attributes_1_id" name="post[comments_attributes][1][id]" type="hidden" value="2" />' +
850
+ '</form>'
851
+
852
+ assert_dom_equal expected, output_buffer
853
+ end
854
+
775
855
  def test_nested_fields_for_on_a_nested_attributes_collection_association_yields_only_builder
776
856
  @post.comments = [Comment.new(321), Comment.new]
777
857
  yielded_comments = []
@@ -786,8 +866,8 @@ class FormHelperTest < ActionView::TestCase
786
866
 
787
867
  expected = '<form action="http://www.example.com" method="post">' +
788
868
  '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' +
789
- '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="321" />' +
790
869
  '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #321" />' +
870
+ '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="321" />' +
791
871
  '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="new comment" />' +
792
872
  '</form>'
793
873
 
@@ -805,8 +885,8 @@ class FormHelperTest < ActionView::TestCase
805
885
  end
806
886
 
807
887
  expected = '<form action="http://www.example.com" method="post">' +
808
- '<input id="post_comments_attributes_abc_id" name="post[comments_attributes][abc][id]" type="hidden" value="321" />' +
809
888
  '<input id="post_comments_attributes_abc_name" name="post[comments_attributes][abc][name]" size="30" type="text" value="comment #321" />' +
889
+ '<input id="post_comments_attributes_abc_id" name="post[comments_attributes][abc][id]" type="hidden" value="321" />' +
810
890
  '</form>'
811
891
 
812
892
  assert_dom_equal expected, output_buffer
@@ -840,18 +920,18 @@ class FormHelperTest < ActionView::TestCase
840
920
  end
841
921
 
842
922
  expected = '<form action="http://www.example.com" method="post">' +
843
- '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="321" />' +
844
923
  '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #321" />' +
845
- '<input id="post_comments_attributes_0_relevances_attributes_0_id" name="post[comments_attributes][0][relevances_attributes][0][id]" type="hidden" value="314" />' +
846
924
  '<input id="post_comments_attributes_0_relevances_attributes_0_value" name="post[comments_attributes][0][relevances_attributes][0][value]" size="30" type="text" value="commentrelevance #314" />' +
847
- '<input id="post_tags_attributes_0_id" name="post[tags_attributes][0][id]" type="hidden" value="123" />' +
925
+ '<input id="post_comments_attributes_0_relevances_attributes_0_id" name="post[comments_attributes][0][relevances_attributes][0][id]" type="hidden" value="314" />' +
926
+ '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="321" />' +
848
927
  '<input id="post_tags_attributes_0_value" name="post[tags_attributes][0][value]" size="30" type="text" value="tag #123" />' +
849
- '<input id="post_tags_attributes_0_relevances_attributes_0_id" name="post[tags_attributes][0][relevances_attributes][0][id]" type="hidden" value="3141" />' +
850
928
  '<input id="post_tags_attributes_0_relevances_attributes_0_value" name="post[tags_attributes][0][relevances_attributes][0][value]" size="30" type="text" value="tagrelevance #3141" />' +
851
- '<input id="post_tags_attributes_1_id" name="post[tags_attributes][1][id]" type="hidden" value="456" />' +
929
+ '<input id="post_tags_attributes_0_relevances_attributes_0_id" name="post[tags_attributes][0][relevances_attributes][0][id]" type="hidden" value="3141" />' +
930
+ '<input id="post_tags_attributes_0_id" name="post[tags_attributes][0][id]" type="hidden" value="123" />' +
852
931
  '<input id="post_tags_attributes_1_value" name="post[tags_attributes][1][value]" size="30" type="text" value="tag #456" />' +
853
- '<input id="post_tags_attributes_1_relevances_attributes_0_id" name="post[tags_attributes][1][relevances_attributes][0][id]" type="hidden" value="31415" />' +
854
932
  '<input id="post_tags_attributes_1_relevances_attributes_0_value" name="post[tags_attributes][1][relevances_attributes][0][value]" size="30" type="text" value="tagrelevance #31415" />' +
933
+ '<input id="post_tags_attributes_1_relevances_attributes_0_id" name="post[tags_attributes][1][relevances_attributes][0][id]" type="hidden" value="31415" />' +
934
+ '<input id="post_tags_attributes_1_id" name="post[tags_attributes][1][id]" type="hidden" value="456" />' +
855
935
  '</form>'
856
936
 
857
937
  assert_dom_equal expected, output_buffer
@@ -1024,7 +1104,7 @@ class FormHelperTest < ActionView::TestCase
1024
1104
  (field_helpers - %w(hidden_field)).each do |selector|
1025
1105
  src = <<-END_SRC
1026
1106
  def #{selector}(field, *args, &proc)
1027
- "<label for='\#{field}'>\#{field.to_s.humanize}:</label> " + super + "<br/>"
1107
+ ("<label for='\#{field}'>\#{field.to_s.humanize}:</label> " + super + "<br/>").html_safe!
1028
1108
  end
1029
1109
  END_SRC
1030
1110
  class_eval src, __FILE__, __LINE__