formtastic 1.2.5 → 2.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. data/.gitignore +13 -0
  2. data/.rspec +2 -0
  3. data/.yardopts +1 -0
  4. data/CHANGELOG +279 -0
  5. data/Gemfile +3 -0
  6. data/README.textile +155 -172
  7. data/RELEASE_PROCESS +7 -0
  8. data/Rakefile +52 -0
  9. data/app/assets/stylesheets/formtastic.css +275 -0
  10. data/app/assets/stylesheets/formtastic_ie6.css +27 -0
  11. data/app/assets/stylesheets/formtastic_ie7.css +17 -0
  12. data/formtastic.gemspec +51 -0
  13. data/lib/formtastic.rb +21 -1960
  14. data/lib/formtastic/engine.rb +7 -0
  15. data/lib/formtastic/form_builder.rb +83 -0
  16. data/lib/formtastic/helpers.rb +16 -0
  17. data/lib/formtastic/helpers/buttons_helper.rb +277 -0
  18. data/lib/formtastic/helpers/errors_helper.rb +113 -0
  19. data/lib/formtastic/helpers/fieldset_wrapper.rb +75 -0
  20. data/lib/formtastic/helpers/file_column_detection.rb +16 -0
  21. data/lib/formtastic/helpers/form_helper.rb +198 -0
  22. data/lib/formtastic/helpers/input_helper.rb +366 -0
  23. data/lib/formtastic/helpers/inputs_helper.rb +392 -0
  24. data/lib/formtastic/helpers/reflection.rb +33 -0
  25. data/lib/formtastic/helpers/semantic_form_helper.rb +11 -0
  26. data/lib/formtastic/html_attributes.rb +21 -0
  27. data/lib/formtastic/i18n.rb +1 -0
  28. data/lib/formtastic/inputs.rb +31 -0
  29. data/lib/formtastic/inputs/base.rb +61 -0
  30. data/lib/formtastic/inputs/base/associations.rb +31 -0
  31. data/lib/formtastic/inputs/base/choices.rb +103 -0
  32. data/lib/formtastic/inputs/base/collections.rb +94 -0
  33. data/lib/formtastic/inputs/base/database.rb +17 -0
  34. data/lib/formtastic/inputs/base/errors.rb +58 -0
  35. data/lib/formtastic/inputs/base/fileish.rb +23 -0
  36. data/lib/formtastic/inputs/base/grouped_collections.rb +77 -0
  37. data/lib/formtastic/inputs/base/hints.rb +31 -0
  38. data/lib/formtastic/inputs/base/html.rb +52 -0
  39. data/lib/formtastic/inputs/base/labelling.rb +55 -0
  40. data/lib/formtastic/inputs/base/naming.rb +42 -0
  41. data/lib/formtastic/inputs/base/options.rb +18 -0
  42. data/lib/formtastic/inputs/base/stringish.rb +35 -0
  43. data/lib/formtastic/inputs/base/timeish.rb +128 -0
  44. data/lib/formtastic/inputs/base/validations.rb +166 -0
  45. data/lib/formtastic/inputs/base/wrapping.rb +40 -0
  46. data/lib/formtastic/inputs/boolean_input.rb +96 -0
  47. data/lib/formtastic/inputs/check_boxes_input.rb +179 -0
  48. data/lib/formtastic/inputs/country_input.rb +66 -0
  49. data/lib/formtastic/inputs/date_input.rb +14 -0
  50. data/lib/formtastic/inputs/datetime_input.rb +9 -0
  51. data/lib/formtastic/inputs/email_input.rb +40 -0
  52. data/lib/formtastic/inputs/file_input.rb +42 -0
  53. data/lib/formtastic/inputs/hidden_input.rb +66 -0
  54. data/lib/formtastic/inputs/number_input.rb +118 -0
  55. data/lib/formtastic/inputs/numeric_input.rb +21 -0
  56. data/lib/formtastic/inputs/password_input.rb +40 -0
  57. data/lib/formtastic/inputs/phone_input.rb +41 -0
  58. data/lib/formtastic/inputs/radio_input.rb +157 -0
  59. data/lib/formtastic/inputs/range_input.rb +119 -0
  60. data/lib/formtastic/inputs/search_input.rb +40 -0
  61. data/lib/formtastic/inputs/select_input.rb +210 -0
  62. data/lib/formtastic/inputs/string_input.rb +34 -0
  63. data/lib/formtastic/inputs/text_input.rb +47 -0
  64. data/lib/formtastic/inputs/time_input.rb +14 -0
  65. data/lib/formtastic/inputs/time_zone_input.rb +48 -0
  66. data/lib/formtastic/inputs/url_input.rb +40 -0
  67. data/lib/formtastic/localized_string.rb +105 -0
  68. data/lib/formtastic/railtie.rb +5 -7
  69. data/lib/formtastic/semantic_form_builder.rb +11 -0
  70. data/lib/formtastic/util.rb +6 -19
  71. data/lib/formtastic/version.rb +3 -0
  72. data/lib/generators/formtastic/install/install_generator.rb +28 -6
  73. data/lib/generators/templates/_form.html.erb +10 -4
  74. data/lib/generators/templates/_form.html.haml +8 -4
  75. data/lib/generators/templates/formtastic.rb +25 -32
  76. data/lib/locale/en.yml +1 -0
  77. data/lib/tasks/verify_rcov.rb +44 -0
  78. data/sample/basic_inputs.html +182 -0
  79. data/sample/config.ru +69 -0
  80. data/sample/index.html +14 -0
  81. data/spec/builder/custom_builder_spec.rb +109 -0
  82. data/spec/builder/error_proc_spec.rb +27 -0
  83. data/spec/builder/errors_spec.rb +193 -0
  84. data/spec/builder/semantic_fields_for_spec.rb +88 -0
  85. data/spec/helpers/buttons_helper_spec.rb +150 -0
  86. data/spec/helpers/commit_button_helper_spec.rb +470 -0
  87. data/spec/helpers/form_helper_spec.rb +135 -0
  88. data/spec/helpers/input_helper_spec.rb +837 -0
  89. data/spec/helpers/inputs_helper_spec.rb +562 -0
  90. data/spec/helpers/semantic_errors_helper_spec.rb +112 -0
  91. data/spec/i18n_spec.rb +199 -0
  92. data/spec/inputs/boolean_input_spec.rb +184 -0
  93. data/spec/inputs/check_boxes_input_spec.rb +375 -0
  94. data/spec/inputs/country_input_spec.rb +133 -0
  95. data/spec/inputs/custom_input_spec.rb +52 -0
  96. data/spec/inputs/date_input_spec.rb +110 -0
  97. data/spec/inputs/datetime_input_spec.rb +115 -0
  98. data/spec/inputs/email_input_spec.rb +55 -0
  99. data/spec/inputs/file_input_spec.rb +59 -0
  100. data/spec/inputs/hidden_input_spec.rb +120 -0
  101. data/spec/inputs/include_blank_spec.rb +70 -0
  102. data/spec/inputs/label_spec.rb +104 -0
  103. data/spec/inputs/number_input_spec.rb +487 -0
  104. data/spec/inputs/numeric_input_spec.rb +41 -0
  105. data/spec/inputs/password_input_spec.rb +69 -0
  106. data/spec/inputs/phone_input_spec.rb +55 -0
  107. data/spec/inputs/placeholder_spec.rb +71 -0
  108. data/spec/inputs/radio_input_spec.rb +234 -0
  109. data/spec/inputs/range_input_spec.rb +477 -0
  110. data/spec/inputs/search_input_spec.rb +55 -0
  111. data/spec/inputs/select_input_spec.rb +545 -0
  112. data/spec/inputs/string_input_spec.rb +163 -0
  113. data/spec/inputs/text_input_spec.rb +158 -0
  114. data/spec/inputs/time_input_spec.rb +155 -0
  115. data/spec/inputs/time_zone_input_spec.rb +87 -0
  116. data/spec/inputs/url_input_spec.rb +55 -0
  117. data/spec/spec.opts +2 -0
  118. data/spec/spec_helper.rb +361 -0
  119. data/spec/support/custom_macros.rb +656 -0
  120. data/spec/support/deferred_garbage_collection.rb +21 -0
  121. data/spec/support/deprecation.rb +6 -0
  122. data/spec/support/test_environment.rb +30 -0
  123. metadata +306 -88
  124. data/generators/form/USAGE +0 -16
  125. data/generators/form/form_generator.rb +0 -111
  126. data/generators/formtastic/formtastic_generator.rb +0 -26
  127. data/init.rb +0 -5
  128. data/lib/formtastic/layout_helper.rb +0 -12
  129. data/lib/generators/formtastic/form/form_generator.rb +0 -84
  130. data/lib/generators/templates/formtastic.css +0 -145
  131. data/lib/generators/templates/formtastic_changes.css +0 -14
  132. data/lib/generators/templates/rails2/_form.html.erb +0 -5
  133. data/lib/generators/templates/rails2/_form.html.haml +0 -4
  134. data/rails/init.rb +0 -2
@@ -0,0 +1,193 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe 'Formtastic::FormBuilder#errors_on' do
5
+
6
+ include FormtasticSpecHelper
7
+
8
+ before do
9
+ @output_buffer = ''
10
+ mock_everything
11
+ @title_errors = ['must not be blank', 'must be longer than 10 characters', 'must be awesome']
12
+ @errors = mock('errors')
13
+ @new_post.stub!(:errors).and_return(@errors)
14
+ end
15
+
16
+ describe 'when there are errors' do
17
+ before do
18
+ @errors.stub!(:[]).with(:title).and_return(@title_errors)
19
+ end
20
+
21
+ after do
22
+ Formtastic::FormBuilder.default_inline_error_class = 'inline-errors'
23
+ Formtastic::FormBuilder.default_error_list_class = 'errors'
24
+ end
25
+
26
+ it 'should render a paragraph with the errors joined into a sentence when inline_errors config is :sentence' do
27
+ Formtastic::FormBuilder.inline_errors = :sentence
28
+ concat(semantic_form_for(@new_post) do |builder|
29
+ builder.input :title
30
+ end)
31
+ output_buffer.should have_tag('p.inline-errors', @title_errors.to_sentence)
32
+ end
33
+
34
+ it 'should render a paragraph with a overridden default class' do
35
+ Formtastic::FormBuilder.inline_errors = :sentence
36
+ Formtastic::FormBuilder.default_inline_error_class = 'better-errors'
37
+ concat(semantic_form_for(@new_post) do |builder|
38
+ builder.input(:title)
39
+ end)
40
+ output_buffer.should have_tag('p.better-errors', @title_errors.to_sentence)
41
+ end
42
+
43
+ it 'should render a paragraph with the errors joined into a sentence when inline_errors config is :sentence with a customized error class' do
44
+ Formtastic::FormBuilder.inline_errors = :sentence
45
+ concat(semantic_form_for(@new_post) do |builder|
46
+ builder.input(:title, :error_class => 'better-errors')
47
+ end)
48
+ output_buffer.should have_tag('p.better-errors', @title_errors.to_sentence)
49
+ end
50
+
51
+ it 'should render an unordered list with the class errors when inline_errors config is :list' do
52
+ Formtastic::FormBuilder.inline_errors = :list
53
+ concat(semantic_form_for(@new_post) do |builder|
54
+ builder.input(:title)
55
+ end)
56
+ output_buffer.should have_tag('ul.errors')
57
+ @title_errors.each do |error|
58
+ output_buffer.should have_tag('ul.errors li', error)
59
+ end
60
+ end
61
+
62
+ it 'should render an unordered list with the class overridden default class' do
63
+ Formtastic::FormBuilder.inline_errors = :list
64
+ Formtastic::FormBuilder.default_error_list_class = "better-errors"
65
+ concat(semantic_form_for(@new_post) do |builder|
66
+ builder.input :title
67
+ end)
68
+ output_buffer.should have_tag('ul.better-errors')
69
+ @title_errors.each do |error|
70
+ output_buffer.should have_tag('ul.better-errors li', error)
71
+ end
72
+ end
73
+
74
+ it 'should render an unordered list with the class errors when inline_errors config is :list with a customized error class' do
75
+ Formtastic::FormBuilder.inline_errors = :list
76
+ concat(semantic_form_for(@new_post) do |builder|
77
+ builder.input :title, :error_class => "better-errors"
78
+ end)
79
+ output_buffer.should have_tag('ul.better-errors')
80
+ @title_errors.each do |error|
81
+ output_buffer.should have_tag('ul.better-errors li', error)
82
+ end
83
+ end
84
+
85
+ it 'should render a paragraph with the first error when inline_errors config is :first' do
86
+ Formtastic::FormBuilder.inline_errors = :first
87
+ concat(semantic_form_for(@new_post) do |builder|
88
+ builder.input :title
89
+ end)
90
+ output_buffer.should have_tag('p.inline-errors', @title_errors.first)
91
+
92
+ end
93
+
94
+ it 'should render a paragraph with the first error when inline_errors config is :first with a customized error class' do
95
+ Formtastic::FormBuilder.inline_errors = :first
96
+ concat(semantic_form_for(@new_post) do |builder|
97
+ builder.input :title, :error_class => "better-errors"
98
+ end)
99
+ output_buffer.should have_tag('p.better-errors', @title_errors.first)
100
+ end
101
+
102
+ it 'should return nil when inline_errors config is :none' do
103
+ Formtastic::FormBuilder.inline_errors = :none
104
+ concat(semantic_form_for(@new_post) do |builder|
105
+ builder.input :title
106
+ end)
107
+ output_buffer.should_not have_tag('p.inine-errors')
108
+ output_buffer.should_not have_tag('ul.errors')
109
+ end
110
+
111
+ end
112
+
113
+ describe 'when there are no errors (nil)' do
114
+ before do
115
+ @errors.stub!(:[]).with(:title).and_return(nil)
116
+ end
117
+
118
+ it 'should return nil when inline_errors config is :sentence, :list or :none' do
119
+ with_deprecation_silenced do
120
+ [:sentence, :list, :none].each do |config|
121
+ with_config :inline_errors, config do
122
+ semantic_form_for(@new_post) do |builder|
123
+ builder.errors_on(:title).should be_nil
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
130
+
131
+ describe 'when there are no errors (empty array)' do
132
+ before do
133
+ @errors.stub!(:[]).with(:title).and_return([])
134
+ end
135
+
136
+ it 'should return nil when inline_errors config is :sentence, :list or :none' do
137
+ with_deprecation_silenced do
138
+ [:sentence, :list, :none].each do |config|
139
+ Formtastic::FormBuilder.inline_errors = config
140
+ semantic_form_for(@new_post) do |builder|
141
+ builder.errors_on(:title).should be_nil
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ describe 'when file type columns have errors' do
149
+ it "should list errors added on metadata fields" do
150
+ @errors.stub!(:[]).with(:document_file_name).and_return(['must be provided'])
151
+ @errors.stub!(:[]).with(:document_file_size).and_return(['must be less than 4mb'])
152
+ @errors.stub!(:[]).with(:document_content_type).and_return(['must be an image'])
153
+ @errors.stub!(:[]).with(:document).and_return(nil)
154
+
155
+ with_config :inline_errors, :sentence do
156
+ concat(semantic_form_for(@new_post) do |builder|
157
+ concat(builder.input(:document))
158
+ end)
159
+ end
160
+ output_buffer.should have_tag("li.error")
161
+ output_buffer.should have_tag('p.inline-errors', (['must be an image','must be provided', 'must be less than 4mb']).to_sentence)
162
+ end
163
+ end
164
+
165
+ describe 'when there are errors on the association and column' do
166
+
167
+ it "should list all unique errors" do
168
+ ::Post.stub!(:reflections).and_return({:author => mock('reflection', :options => {}, :macro => :belongs_to)})
169
+
170
+ @errors.stub!(:[]).with(:author).and_return(['must not be blank'])
171
+ @errors.stub!(:[]).with(:author_id).and_return(['is already taken', 'must not be blank']) # note the duplicate of association
172
+
173
+ with_config :inline_errors, :list do
174
+ concat(semantic_form_for(@new_post) do |builder|
175
+ concat(builder.input(:author))
176
+ end)
177
+ end
178
+ output_buffer.should have_tag("ul.errors li", /must not be blank/, :count => 1)
179
+ output_buffer.should have_tag("ul.errors li", /is already taken/, :count => 1)
180
+ end
181
+
182
+ end
183
+
184
+ describe "when there are errors on a has_many association" do
185
+ it "should include the association ids error messages" do
186
+ semantic_form_for(@new_post) do |builder|
187
+ builder.send(:error_keys, :sub_posts, {}).should include(:sub_post_ids)
188
+ end
189
+ end
190
+ end
191
+
192
+ end
193
+
@@ -0,0 +1,88 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe 'Formtastic::FormBuilder#fields_for' do
5
+
6
+ include FormtasticSpecHelper
7
+
8
+ before do
9
+ @output_buffer = ''
10
+ mock_everything
11
+ @new_post.stub!(:author).and_return(::Author.new)
12
+ end
13
+
14
+ it 'yields an instance of FormHelper.builder' do
15
+ semantic_form_for(@new_post) do |builder|
16
+ builder.fields_for(:author) do |nested_builder|
17
+ nested_builder.class.should == Formtastic::Helpers::FormHelper.builder
18
+ end
19
+ end
20
+ end
21
+
22
+ it 'nests the object name' do
23
+ semantic_form_for(@new_post) do |builder|
24
+ builder.fields_for(@bob) do |nested_builder|
25
+ nested_builder.object_name.should == 'post[author]'
26
+ end
27
+ end
28
+ end
29
+
30
+ it 'supports passing collection as second parameter' do
31
+ semantic_form_for(@new_post) do |builder|
32
+ builder.semantic_fields_for(:author, [@fred,@bob]) do |nested_builder|
33
+ nested_builder.object_name.should =~ /post\[author_attributes\]\[\d+\]/
34
+ end
35
+ end
36
+ end
37
+
38
+ it 'should sanitize html id for li tag' do
39
+ @bob.stub!(:column_for_attribute).and_return(mock('column', :type => :string, :limit => 255))
40
+ concat(semantic_form_for(@new_post) do |builder|
41
+ concat(builder.fields_for(@bob, :index => 1) do |nested_builder|
42
+ concat(nested_builder.inputs(:login))
43
+ end)
44
+ end)
45
+ output_buffer.should have_tag('form fieldset.inputs #post_author_1_login_input')
46
+ # Not valid selector, so using good ol' regex
47
+ output_buffer.should_not =~ /id="post\[author\]_1_login_input"/
48
+ # <=> output_buffer.should_not have_tag('form fieldset.inputs #post[author]_1_login_input')
49
+ end
50
+
51
+ it 'should use namespace provided in nested fields' do
52
+ @bob.stub!(:column_for_attribute).and_return(mock('column', :type => :string, :limit => 255))
53
+ concat(semantic_form_for(@new_post, :namespace => 'context2') do |builder|
54
+ concat(builder.fields_for(@bob, :index => 1) do |nested_builder|
55
+ concat(nested_builder.inputs(:login))
56
+ end)
57
+ end)
58
+ output_buffer.should have_tag('form fieldset.inputs #context2_post_author_1_login_input')
59
+ end
60
+
61
+ context "when I rendered my own hidden id input" do
62
+
63
+ before do
64
+ output_buffer.replace ''
65
+
66
+ @fred.posts.size.should == 1
67
+ @fred.posts.first.stub!(:persisted?).and_return(true)
68
+ @fred.stub!(:posts_attributes=)
69
+
70
+ concat(semantic_form_for(@fred) do |builder|
71
+ concat(builder.fields_for(:posts) do |nested_builder|
72
+ concat(nested_builder.input(:id, :as => :hidden))
73
+ concat(nested_builder.input(:title))
74
+ end)
75
+ end)
76
+ end
77
+
78
+ it "should only render one hidden input (my one)" do
79
+ output_buffer.should have_tag 'input#author_posts_attributes_0_id', :count => 1
80
+ end
81
+
82
+ it "should render the hidden input inside an li.hidden" do
83
+ output_buffer.should have_tag 'li.hidden input#author_posts_attributes_0_id'
84
+ end
85
+ end
86
+
87
+ end
88
+
@@ -0,0 +1,150 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe 'Formtastic::FormBuilder#buttons' do
5
+
6
+ include FormtasticSpecHelper
7
+
8
+ before do
9
+ @output_buffer = ''
10
+ mock_everything
11
+ end
12
+
13
+ describe 'with a block' do
14
+ describe 'when no options are provided' do
15
+ before do
16
+ concat(semantic_form_for(@new_post) do |builder|
17
+ buttons = builder.buttons do
18
+ concat('hello')
19
+ end
20
+ concat(buttons)
21
+ end)
22
+ end
23
+
24
+ it 'should render a fieldset inside the form, with a class of "inputs"' do
25
+ output_buffer.should have_tag("form fieldset.buttons")
26
+ end
27
+
28
+ it 'should render an ol inside the fieldset' do
29
+ output_buffer.should have_tag("form fieldset.buttons ol")
30
+ end
31
+
32
+ it 'should render the contents of the block inside the ol' do
33
+ output_buffer.should have_tag("form fieldset.buttons ol", /hello/)
34
+ end
35
+
36
+ it 'should not render a legend inside the fieldset' do
37
+ output_buffer.should_not have_tag("form fieldset.buttons legend")
38
+ end
39
+ end
40
+
41
+ describe 'when a :name option is provided' do
42
+ before do
43
+ @legend_text = "Advanced options"
44
+
45
+ concat(semantic_form_for(@new_post) do |builder|
46
+ builder.buttons :name => @legend_text do
47
+ end
48
+ end)
49
+ end
50
+ it 'should render a fieldset inside the form' do
51
+ output_buffer.should have_tag("form fieldset legend", /#{@legend_text}/)
52
+ end
53
+ end
54
+
55
+ describe 'when other options are provided' do
56
+ before do
57
+ @id_option = 'advanced'
58
+ @class_option = 'wide'
59
+
60
+ concat(semantic_form_for(@new_post) do |builder|
61
+ builder.buttons :id => @id_option, :class => @class_option do
62
+ end
63
+ end)
64
+ end
65
+ it 'should pass the options into the fieldset tag as attributes' do
66
+ output_buffer.should have_tag("form fieldset##{@id_option}")
67
+ output_buffer.should have_tag("form fieldset.#{@class_option}")
68
+ end
69
+ end
70
+
71
+ end
72
+
73
+ describe 'without a block' do
74
+
75
+ describe 'with no args (default buttons)' do
76
+
77
+ before do
78
+ concat(semantic_form_for(@new_post) do |builder|
79
+ concat(builder.buttons)
80
+ end)
81
+ end
82
+
83
+ it 'should render a form' do
84
+ output_buffer.should have_tag('form')
85
+ end
86
+
87
+ it 'should render a buttons fieldset inside the form' do
88
+ output_buffer.should have_tag('form fieldset.buttons')
89
+ end
90
+
91
+ it 'should not render a legend in the fieldset' do
92
+ output_buffer.should_not have_tag('form fieldset.buttons legend')
93
+ end
94
+
95
+ it 'should render an ol in the fieldset' do
96
+ output_buffer.should have_tag('form fieldset.buttons ol')
97
+ end
98
+
99
+ it 'should render a list item in the ol for each default button' do
100
+ output_buffer.should have_tag('form fieldset.buttons ol li', :count => 1)
101
+ end
102
+
103
+ it 'should render a commit list item for the commit button' do
104
+ output_buffer.should have_tag('form fieldset.buttons ol li.commit')
105
+ end
106
+
107
+ end
108
+
109
+ describe 'with button names as args' do
110
+
111
+ before do
112
+ concat(semantic_form_for(@new_post) do |builder|
113
+ concat(builder.buttons(:commit))
114
+ end)
115
+ end
116
+
117
+ it 'should render a form with a fieldset containing a list item for each button arg' do
118
+ output_buffer.should have_tag('form > fieldset.buttons > ol > li', :count => 1)
119
+ output_buffer.should have_tag('form > fieldset.buttons > ol > li.commit')
120
+ end
121
+
122
+ end
123
+
124
+ describe 'with button names as args and an options hash' do
125
+
126
+ before do
127
+ concat(semantic_form_for(@new_post) do |builder|
128
+ concat(builder.buttons(:commit, :name => "Now click a button", :id => "my-id"))
129
+ end)
130
+ end
131
+
132
+ it 'should render a form with a fieldset containing a list item for each button arg' do
133
+ output_buffer.should have_tag('form > fieldset.buttons > ol > li', :count => 1)
134
+ output_buffer.should have_tag('form > fieldset.buttons > ol > li.commit', :count => 1)
135
+ end
136
+
137
+ it 'should pass the options down to the fieldset' do
138
+ output_buffer.should have_tag('form > fieldset#my-id.buttons')
139
+ end
140
+
141
+ it 'should use the special :name option as a text for the legend tag' do
142
+ output_buffer.should have_tag('form > fieldset#my-id.buttons > legend', /Now click a button/)
143
+ end
144
+
145
+ end
146
+
147
+ end
148
+
149
+ end
150
+
@@ -0,0 +1,470 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe 'Formtastic::FormBuilder#commit_button' do
5
+
6
+ include FormtasticSpecHelper
7
+
8
+ before do
9
+ @output_buffer = ''
10
+ mock_everything
11
+ end
12
+
13
+ describe 'when the object responds to :persisted? (ActiveModel)' do
14
+
15
+ before do
16
+ @new_post.stub(:respond_to?).with(:to_model).and_return("X")
17
+ @new_post.stub(:respond_to?).with(:persisted?).and_return(true)
18
+ @new_post.stub(:respond_to?).with(:new_record?).and_return(false)
19
+ end
20
+
21
+ it 'should call :persisted?' do
22
+ with_config :i18n_lookups_by_default, false do
23
+ @new_post.should_receive(:persisted?)
24
+ @new_post.should_not_receive(:new_record?)
25
+ semantic_form_for(@new_post) do |builder|
26
+ concat(builder.commit_button)
27
+ end
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+ describe 'when not persisted' do
34
+
35
+ before do
36
+ @new_post.stub(:respond_to?).with(:to_model).and_return("X")
37
+ @new_post.stub(:respond_to?).with(:persisted?).and_return(false)
38
+ @new_post.stub(:respond_to?).with(:new_record?).and_return(false)
39
+ end
40
+
41
+ it 'should have a submit button label' do
42
+ with_config :i18n_lookups_by_default, false do
43
+ concat(semantic_form_for(@new_post) do |builder|
44
+ concat(builder.commit_button)
45
+ end)
46
+ end
47
+
48
+ output_buffer.should have_tag('.commit input[@value="Submit Post"]')
49
+ end
50
+ end
51
+
52
+
53
+ describe 'when used on any record' do
54
+
55
+ before do
56
+ @new_post.stub!(:new_record?).and_return(false)
57
+ concat(semantic_form_for(@new_post) do |builder|
58
+ concat(builder.commit_button)
59
+ end)
60
+ end
61
+
62
+ it 'should render a commit li' do
63
+ output_buffer.should have_tag('li.commit')
64
+ end
65
+
66
+ it 'should render a button li' do
67
+ output_buffer.should have_tag('li.button')
68
+ end
69
+
70
+ it 'should render an input with a type attribute of "submit"' do
71
+ output_buffer.should have_tag('li.commit input[@type="submit"]')
72
+ end
73
+
74
+ it 'should render an input with a name attribute of "commit"' do
75
+ output_buffer.should have_tag('li.commit input[@name="commit"]')
76
+ end
77
+
78
+ it 'should pass options given in :button_html to the button' do
79
+ @new_post.stub!(:new_record?).and_return(false)
80
+ concat(semantic_form_for(@new_post) do |builder|
81
+ concat(builder.commit_button('text', :button_html => {:class => 'my_class', :id => 'my_id'}))
82
+ end)
83
+
84
+ output_buffer.should have_tag('li.commit input#my_id')
85
+ output_buffer.should have_tag('li.commit input.my_class')
86
+ end
87
+
88
+ end
89
+
90
+ describe "its accesskey" do
91
+
92
+ it 'should allow nil default' do
93
+ with_config :default_commit_button_accesskey, nil do
94
+ output_buffer.should_not have_tag('li.commit input[@accesskey]')
95
+ end
96
+ end
97
+
98
+ it 'should use the default if set' do
99
+ with_config :default_commit_button_accesskey, 's' do
100
+ @new_post.stub!(:new_record?).and_return(false)
101
+ concat(semantic_form_for(@new_post) do |builder|
102
+ concat(builder.commit_button('text', :button_html => {}))
103
+ end)
104
+ output_buffer.should have_tag('li.commit input[@accesskey="s"]')
105
+ end
106
+ end
107
+
108
+ it 'should use the value set in options over the default' do
109
+ with_config :default_commit_button_accesskey, 's' do
110
+ @new_post.stub!(:new_record?).and_return(false)
111
+ concat(semantic_form_for(@new_post) do |builder|
112
+ concat(builder.commit_button('text', :accesskey => 'o'))
113
+ end)
114
+ output_buffer.should_not have_tag('li.commit input[@accesskey="s"]')
115
+ output_buffer.should have_tag('li.commit input[@accesskey="o"]')
116
+ end
117
+ end
118
+
119
+ it 'should use the value set in button_html over options' do
120
+ with_config :default_commit_button_accesskey, 's' do
121
+ @new_post.stub!(:new_record?).and_return(false)
122
+ concat(semantic_form_for(@new_post) do |builder|
123
+ concat(builder.commit_button('text', :accesskey => 'o', :button_html => {:accesskey => 't'}))
124
+ end)
125
+ output_buffer.should_not have_tag('li.commit input[@accesskey="s"]')
126
+ output_buffer.should_not have_tag('li.commit input[@accesskey="o"]')
127
+ output_buffer.should have_tag('li.commit input[@accesskey="t"]')
128
+ end
129
+ end
130
+
131
+ end
132
+
133
+ describe 'when the first option is a string and the second is a hash' do
134
+
135
+ before do
136
+ @new_post.stub!(:new_record?).and_return(false)
137
+ concat(semantic_form_for(@new_post) do |builder|
138
+ concat(builder.commit_button("a string", :button_html => { :class => "pretty"}))
139
+ end)
140
+ end
141
+
142
+ it "should render the string as the value of the button" do
143
+ output_buffer.should have_tag('li input[@value="a string"]')
144
+ end
145
+
146
+ it "should deal with the options hash" do
147
+ output_buffer.should have_tag('li input.pretty')
148
+ end
149
+
150
+ end
151
+
152
+ describe 'when the first option is a hash' do
153
+
154
+ before do
155
+ @new_post.stub!(:new_record?).and_return(false)
156
+ concat(semantic_form_for(@new_post) do |builder|
157
+ concat(builder.commit_button(:button_html => { :class => "pretty"}))
158
+ end)
159
+ end
160
+
161
+ it "should deal with the options hash" do
162
+ output_buffer.should have_tag('li input.pretty')
163
+ end
164
+
165
+ end
166
+
167
+ describe 'label' do
168
+
169
+ # No object
170
+ describe 'when used without object' do
171
+ describe 'when explicit label is provided' do
172
+ it 'should render an input with the explicitly specified label' do
173
+ concat(semantic_form_for(:post, :url => 'http://example.com') do |builder|
174
+ concat(builder.commit_button("Click!"))
175
+ end)
176
+ output_buffer.should have_tag('li.commit input[@value="Click!"][@class~="submit"]')
177
+ end
178
+ end
179
+
180
+ describe 'when no explicit label is provided' do
181
+ describe 'when no I18n-localized label is provided' do
182
+ before do
183
+ ::I18n.backend.store_translations :en, :formtastic => {:submit => 'Submit %{model}'}
184
+ end
185
+
186
+ after do
187
+ ::I18n.backend.reload!
188
+ end
189
+
190
+ it 'should render an input with default I18n-localized label (fallback)' do
191
+ concat(semantic_form_for(:post, :url => 'http://example.com') do |builder|
192
+ concat(builder.commit_button)
193
+ end)
194
+ output_buffer.should have_tag('li.commit input[@value="Submit Post"][@class~="submit"]')
195
+ end
196
+ end
197
+
198
+ describe 'when I18n-localized label is provided' do
199
+ before do
200
+ ::I18n.backend.store_translations :en,
201
+ :formtastic => {
202
+ :actions => {
203
+ :submit => 'Custom Submit',
204
+ }
205
+ }
206
+ end
207
+
208
+ after do
209
+ ::I18n.backend.reload!
210
+ end
211
+
212
+ it 'should render an input with localized label (I18n)' do
213
+ with_config :i18n_lookups_by_default, true do
214
+ ::I18n.backend.store_translations :en,
215
+ :formtastic => {
216
+ :actions => {
217
+ :post => {
218
+ :submit => 'Custom Submit %{model}'
219
+ }
220
+ }
221
+ }
222
+ concat(semantic_form_for(:post, :url => 'http://example.com') do |builder|
223
+ concat(builder.commit_button)
224
+ end)
225
+ output_buffer.should have_tag(%Q{li.commit input[@value="Custom Submit Post"][@class~="submit"]})
226
+ end
227
+ end
228
+
229
+ it 'should render an input with anoptional localized label (I18n) - if first is not set' do
230
+ with_config :i18n_lookups_by_default, true do
231
+ concat(semantic_form_for(:post, :url => 'http://example.com') do |builder|
232
+ concat(builder.commit_button)
233
+ end)
234
+ output_buffer.should have_tag(%Q{li.commit input[@value="Custom Submit"][@class~="submit"]})
235
+ end
236
+ end
237
+
238
+ end
239
+ end
240
+ end
241
+
242
+ # New record
243
+ describe 'when used on a new record' do
244
+ before do
245
+ @new_post.stub!(:new_record?).and_return(true)
246
+ end
247
+
248
+ describe 'when explicit label is provided' do
249
+ it 'should render an input with the explicitly specified label' do
250
+ concat(semantic_form_for(@new_post) do |builder|
251
+ concat(builder.commit_button("Click!"))
252
+ end)
253
+ output_buffer.should have_tag('li.commit input[@value="Click!"][@class~="create"]')
254
+ end
255
+ end
256
+
257
+ describe 'when no explicit label is provided' do
258
+ describe 'when no I18n-localized label is provided' do
259
+ before do
260
+ ::I18n.backend.store_translations :en, :formtastic => {:create => 'Create %{model}'}
261
+ end
262
+
263
+ after do
264
+ ::I18n.backend.reload!
265
+ end
266
+
267
+ it 'should render an input with default I18n-localized label (fallback)' do
268
+ concat(semantic_form_for(@new_post) do |builder|
269
+ concat(builder.commit_button)
270
+ end)
271
+ output_buffer.should have_tag('li.commit input[@value="Create Post"][@class~="create"]')
272
+ end
273
+ end
274
+
275
+ describe 'when I18n-localized label is provided' do
276
+ before do
277
+ ::I18n.backend.store_translations :en,
278
+ :formtastic => {
279
+ :actions => {
280
+ :create => 'Custom Create',
281
+ }
282
+ }
283
+ end
284
+
285
+ after do
286
+ ::I18n.backend.reload!
287
+ end
288
+
289
+ it 'should render an input with localized label (I18n)' do
290
+ with_config :i18n_lookups_by_default, true do
291
+ ::I18n.backend.store_translations :en,
292
+ :formtastic => {
293
+ :actions => {
294
+ :post => {
295
+ :create => 'Custom Create %{model}'
296
+ }
297
+ }
298
+ }
299
+ concat(semantic_form_for(@new_post) do |builder|
300
+ concat(builder.commit_button)
301
+ end)
302
+ output_buffer.should have_tag(%Q{li.commit input[@value="Custom Create Post"][@class~="create"]})
303
+ end
304
+ end
305
+
306
+ it 'should render an input with anoptional localized label (I18n) - if first is not set' do
307
+ with_config :i18n_lookups_by_default, true do
308
+ concat(semantic_form_for(@new_post) do |builder|
309
+ concat(builder.commit_button)
310
+ end)
311
+ output_buffer.should have_tag(%Q{li.commit input[@value="Custom Create"][@class~="create"]})
312
+ end
313
+ end
314
+
315
+ end
316
+ end
317
+ end
318
+
319
+ # Existing record
320
+ describe 'when used on an existing record' do
321
+ before do
322
+ @new_post.stub!(:persisted?).and_return(true)
323
+ end
324
+
325
+ describe 'when explicit label is provided' do
326
+ it 'should render an input with the explicitly specified label' do
327
+ concat(semantic_form_for(@new_post) do |builder|
328
+ concat(builder.commit_button("Click!"))
329
+ end)
330
+ output_buffer.should have_tag('li.commit input[@value="Click!"][@class~="update"]')
331
+ end
332
+ end
333
+
334
+ describe 'when no explicit label is provided' do
335
+ describe 'when no I18n-localized label is provided' do
336
+ before do
337
+ ::I18n.backend.store_translations :en, :formtastic => {:update => 'Save %{model}'}
338
+ end
339
+
340
+ after do
341
+ ::I18n.backend.reload!
342
+ end
343
+
344
+ it 'should render an input with default I18n-localized label (fallback)' do
345
+ concat(semantic_form_for(@new_post) do |builder|
346
+ concat(builder.commit_button)
347
+ end)
348
+ output_buffer.should have_tag('li.commit input[@value="Save Post"][@class~="update"]')
349
+ end
350
+ end
351
+
352
+ describe 'when I18n-localized label is provided' do
353
+ before do
354
+ ::I18n.backend.reload!
355
+ ::I18n.backend.store_translations :en,
356
+ :formtastic => {
357
+ :actions => {
358
+ :update => 'Custom Save',
359
+ }
360
+ }
361
+ end
362
+
363
+ after do
364
+ ::I18n.backend.reload!
365
+ end
366
+
367
+ it 'should render an input with localized label (I18n)' do
368
+ with_config :i18n_lookups_by_default, true do
369
+ ::I18n.backend.store_translations :en,
370
+ :formtastic => {
371
+ :actions => {
372
+ :post => {
373
+ :update => 'Custom Save %{model}'
374
+ }
375
+ }
376
+ }
377
+ concat(semantic_form_for(@new_post) do |builder|
378
+ concat(builder.commit_button)
379
+ end)
380
+ output_buffer.should have_tag(%Q{li.commit input[@value="Custom Save Post"][@class~="update"]})
381
+ end
382
+ end
383
+
384
+ it 'should render an input with anoptional localized label (I18n) - if first is not set' do
385
+ with_config :i18n_lookups_by_default, true do
386
+ concat(semantic_form_for(@new_post) do |builder|
387
+ concat(builder.commit_button)
388
+ end)
389
+ output_buffer.should have_tag(%Q{li.commit input[@value="Custom Save"][@class~="update"]})
390
+ ::I18n.backend.store_translations :en, :formtastic => {}
391
+ end
392
+ end
393
+
394
+ end
395
+ end
396
+ end
397
+ end
398
+
399
+ describe 'when the model is two words' do
400
+ before do
401
+ output_buffer = ''
402
+ class ::UserPost
403
+ extend ActiveModel::Naming if defined?(ActiveModel::Naming)
404
+ include ActiveModel::Conversion if defined?(ActiveModel::Conversion)
405
+
406
+ def id
407
+ end
408
+
409
+ def persisted?
410
+ end
411
+
412
+ # Rails does crappy human_name
413
+ def self.human_name
414
+ "User post"
415
+ end
416
+ end
417
+ @new_user_post = ::UserPost.new
418
+
419
+ @new_user_post.stub!(:new_record?).and_return(true)
420
+ concat(semantic_form_for(@new_user_post, :url => '') do |builder|
421
+ concat(builder.commit_button())
422
+ end)
423
+ end
424
+
425
+ it "should render the string as the value of the button" do
426
+ output_buffer.should have_tag('li input[@value="Create User post"]')
427
+ end
428
+
429
+ end
430
+
431
+ describe ':wrapper_html option' do
432
+
433
+ describe 'when provided' do
434
+ it 'should be passed down to the li tag' do
435
+ concat(semantic_form_for(@new_post) do |builder|
436
+ concat(builder.commit_button('text', :wrapper_html => {:id => :another_id}))
437
+ end)
438
+ output_buffer.should have_tag("form li#another_id")
439
+ end
440
+
441
+ it 'should append given classes to li default classes' do
442
+ concat(semantic_form_for(@new_post) do |builder|
443
+ concat(builder.commit_button('text', :wrapper_html => {:class => :another_class}))
444
+ end)
445
+ output_buffer.should have_tag("form li.commit")
446
+ output_buffer.should have_tag("form li.another_class")
447
+ end
448
+
449
+ it 'should allow classes to be an array' do
450
+ concat(semantic_form_for(@new_post) do |builder|
451
+ concat(builder.commit_button('text', :wrapper_html => {:class => [ :my_class, :another_class ]}))
452
+ end)
453
+ output_buffer.should have_tag("form li.commit")
454
+ output_buffer.should have_tag("form li.my_class")
455
+ output_buffer.should have_tag("form li.another_class")
456
+ end
457
+ end
458
+
459
+ describe 'when not provided' do
460
+ it 'should use default id and class' do
461
+ concat(semantic_form_for(@new_post) do |builder|
462
+ concat(builder.commit_button('text'))
463
+ end)
464
+ output_buffer.should have_tag("form li.commit")
465
+ end
466
+ end
467
+
468
+ end
469
+
470
+ end