tmayad-formtastic 0.9.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.textile +517 -0
  3. data/Rakefile +101 -0
  4. data/generators/form/USAGE +16 -0
  5. data/generators/form/form_generator.rb +120 -0
  6. data/generators/form/templates/view__form.html.erb +5 -0
  7. data/generators/form/templates/view__form.html.haml +4 -0
  8. data/generators/formtastic/formtastic_generator.rb +24 -0
  9. data/generators/formtastic/templates/formtastic.css +138 -0
  10. data/generators/formtastic/templates/formtastic.rb +54 -0
  11. data/generators/formtastic/templates/formtastic_changes.css +10 -0
  12. data/generators/formtastic_stylesheets/formtastic_stylesheets_generator.rb +16 -0
  13. data/lib/formtastic.rb +1643 -0
  14. data/lib/formtastic/i18n.rb +32 -0
  15. data/lib/locale/en.yml +8 -0
  16. data/rails/init.rb +3 -0
  17. data/spec/buttons_spec.rb +149 -0
  18. data/spec/commit_button_spec.rb +344 -0
  19. data/spec/custom_builder_spec.rb +62 -0
  20. data/spec/custom_macros.rb +561 -0
  21. data/spec/defaults_spec.rb +20 -0
  22. data/spec/error_proc_spec.rb +27 -0
  23. data/spec/errors_spec.rb +85 -0
  24. data/spec/form_helper_spec.rb +120 -0
  25. data/spec/i18n_spec.rb +131 -0
  26. data/spec/include_blank_spec.rb +70 -0
  27. data/spec/input_spec.rb +608 -0
  28. data/spec/inputs/boolean_input_spec.rb +93 -0
  29. data/spec/inputs/check_boxes_input_spec.rb +162 -0
  30. data/spec/inputs/country_input_spec.rb +80 -0
  31. data/spec/inputs/date_input_spec.rb +45 -0
  32. data/spec/inputs/datetime_input_spec.rb +155 -0
  33. data/spec/inputs/file_input_spec.rb +33 -0
  34. data/spec/inputs/hidden_input_spec.rb +52 -0
  35. data/spec/inputs/numeric_input_spec.rb +44 -0
  36. data/spec/inputs/password_input_spec.rb +46 -0
  37. data/spec/inputs/radio_input_spec.rb +149 -0
  38. data/spec/inputs/select_input_spec.rb +459 -0
  39. data/spec/inputs/string_input_spec.rb +47 -0
  40. data/spec/inputs/text_input_spec.rb +33 -0
  41. data/spec/inputs/time_input_spec.rb +44 -0
  42. data/spec/inputs/time_zone_input_spec.rb +102 -0
  43. data/spec/inputs_spec.rb +395 -0
  44. data/spec/label_spec.rb +48 -0
  45. data/spec/semantic_fields_for_spec.rb +44 -0
  46. data/spec/spec.opts +2 -0
  47. data/spec/spec_helper.rb +212 -0
  48. metadata +169 -0
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ require File.dirname(__FILE__) + '/../spec_helper'
3
+
4
+ describe 'file input' do
5
+
6
+ include FormtasticSpecHelper
7
+
8
+ before do
9
+ @output_buffer = ''
10
+ mock_everything
11
+
12
+ semantic_form_for(@new_post) do |builder|
13
+ concat(builder.input(:body, :as => :file))
14
+ end
15
+ end
16
+
17
+ it_should_have_input_wrapper_with_class("file")
18
+ it_should_have_input_wrapper_with_id("post_body_input")
19
+ it_should_have_label_with_text(/Body/)
20
+ it_should_have_label_for("post_body")
21
+ it_should_have_input_with_id("post_body")
22
+ it_should_have_input_with_name("post[body]")
23
+ it_should_apply_error_logic_for_input_type(:file)
24
+
25
+ it 'should use input_html to style inputs' do
26
+ semantic_form_for(@new_post) do |builder|
27
+ concat(builder.input(:title, :as => :file, :input_html => { :class => 'myclass' }))
28
+ end
29
+ output_buffer.should have_tag("form li input.myclass")
30
+ end
31
+
32
+ end
33
+
@@ -0,0 +1,52 @@
1
+ # coding: utf-8
2
+ require File.dirname(__FILE__) + '/../spec_helper'
3
+
4
+ describe 'hidden input' do
5
+
6
+ include FormtasticSpecHelper
7
+
8
+ before do
9
+ @output_buffer = ''
10
+ mock_everything
11
+
12
+ semantic_form_for(@new_post) do |builder|
13
+ concat(builder.input(:secret, :as => :hidden))
14
+ concat(builder.input(:author_id, :as => :hidden, :value => 99))
15
+ concat(builder.input(:published, :as => :hidden, :input_html => {:value => true}))
16
+ end
17
+ end
18
+
19
+ it_should_have_input_wrapper_with_class("hidden")
20
+ it_should_have_input_wrapper_with_id("post_secret_input")
21
+ it_should_not_have_a_label
22
+
23
+ it "should generate a input field" do
24
+ output_buffer.should have_tag("form li input#post_secret")
25
+ output_buffer.should have_tag("form li input#post_secret[@type=\"hidden\"]")
26
+ output_buffer.should have_tag("form li input#post_secret[@name=\"post[secret]\"]")
27
+ end
28
+
29
+ it "should pass any explicitly specified value - using :value" do
30
+ output_buffer.should have_tag("form li input#post_author_id[@type=\"hidden\"][@value=\"99\"]")
31
+ end
32
+
33
+ # Handle Formtastic :input_html options for consistency.
34
+ it "should pass any explicitly specified value - using :input_html options" do
35
+ output_buffer.should have_tag("form li input#post_published[@type=\"hidden\"][@value=\"true\"]")
36
+ end
37
+
38
+ it "should not render inline errors" do
39
+ @errors = mock('errors')
40
+ @errors.stub!(:[]).with(:secret).and_return(["foo", "bah"])
41
+ @new_post.stub!(:errors).and_return(@errors)
42
+
43
+ semantic_form_for(@new_post) do |builder|
44
+ concat(builder.input(:secret, :as => :hidden))
45
+ end
46
+
47
+ output_buffer.should_not have_tag("form li p.inline-errors")
48
+ output_buffer.should_not have_tag("form li ul.errors")
49
+ end
50
+
51
+ end
52
+
@@ -0,0 +1,44 @@
1
+ # coding: utf-8
2
+ require File.dirname(__FILE__) + '/../spec_helper'
3
+
4
+ describe 'numeric input' do
5
+
6
+ include FormtasticSpecHelper
7
+
8
+ before do
9
+ @output_buffer = ''
10
+ mock_everything
11
+
12
+ semantic_form_for(@new_post) do |builder|
13
+ concat(builder.input(:title, :as => :numeric))
14
+ end
15
+ end
16
+
17
+ it_should_have_input_wrapper_with_class(:numeric)
18
+ it_should_have_input_wrapper_with_id("post_title_input")
19
+ it_should_have_label_with_text(/Title/)
20
+ it_should_have_label_for("post_title")
21
+ it_should_have_input_with_id("post_title")
22
+ it_should_have_input_with_type(:text)
23
+ it_should_have_input_with_name("post[title]")
24
+ it_should_use_default_text_field_size_when_method_has_no_database_column(:string)
25
+ it_should_apply_custom_input_attributes_when_input_html_provided(:string)
26
+ it_should_apply_custom_for_to_label_when_input_html_id_provided(:string)
27
+ it_should_apply_error_logic_for_input_type(:numeric)
28
+
29
+ describe "when no object is provided" do
30
+ before do
31
+ semantic_form_for(:project, :url => 'http://test.host/') do |builder|
32
+ concat(builder.input(:title, :as => :numeric))
33
+ end
34
+ end
35
+
36
+ it_should_have_label_with_text(/Title/)
37
+ it_should_have_label_for("project_title")
38
+ it_should_have_input_with_id("project_title")
39
+ it_should_have_input_with_type(:text)
40
+ it_should_have_input_with_name("project[title]")
41
+ end
42
+
43
+ end
44
+
@@ -0,0 +1,46 @@
1
+ # coding: utf-8
2
+ require File.dirname(__FILE__) + '/../spec_helper'
3
+
4
+ describe 'password input' do
5
+
6
+ include FormtasticSpecHelper
7
+
8
+ before do
9
+ @output_buffer = ''
10
+ mock_everything
11
+
12
+ semantic_form_for(@new_post) do |builder|
13
+ concat(builder.input(:title, :as => :password))
14
+ end
15
+ end
16
+
17
+ it_should_have_input_wrapper_with_class(:password)
18
+ it_should_have_input_wrapper_with_id("post_title_input")
19
+ it_should_have_label_with_text(/Title/)
20
+ it_should_have_label_for("post_title")
21
+ it_should_have_input_with_id("post_title")
22
+ it_should_have_input_with_type(:password)
23
+ it_should_have_input_with_name("post[title]")
24
+ it_should_have_maxlength_matching_column_limit
25
+ it_should_use_default_text_field_size_for_columns_longer_than_default_text_field_size(:string)
26
+ it_should_use_column_size_for_columns_shorter_than_default_text_field_size(:string)
27
+ it_should_use_default_text_field_size_when_method_has_no_database_column(:string)
28
+ it_should_apply_custom_input_attributes_when_input_html_provided(:string)
29
+ it_should_apply_custom_for_to_label_when_input_html_id_provided(:string)
30
+ it_should_apply_error_logic_for_input_type(:password)
31
+
32
+ describe "when no object is provided" do
33
+ before do
34
+ semantic_form_for(:project, :url => 'http://test.host/') do |builder|
35
+ concat(builder.input(:title, :as => :password))
36
+ end
37
+ end
38
+
39
+ it_should_have_label_with_text(/Title/)
40
+ it_should_have_label_for("project_title")
41
+ it_should_have_input_with_id("project_title")
42
+ it_should_have_input_with_type(:password)
43
+ it_should_have_input_with_name("project[title]")
44
+ end
45
+
46
+ end
@@ -0,0 +1,149 @@
1
+ # coding: utf-8
2
+ require File.dirname(__FILE__) + '/../spec_helper'
3
+
4
+ describe 'radio input' do
5
+
6
+ include FormtasticSpecHelper
7
+
8
+ before do
9
+ @output_buffer = ''
10
+ mock_everything
11
+ end
12
+
13
+ describe 'for belongs_to association' do
14
+ before do
15
+ semantic_form_for(@new_post) do |builder|
16
+ concat(builder.input(:author, :as => :radio, :value_as_class => true))
17
+ end
18
+ end
19
+
20
+ it_should_have_input_wrapper_with_class("radio")
21
+ it_should_have_input_wrapper_with_id("post_author_input")
22
+ it_should_have_a_nested_fieldset
23
+ it_should_apply_error_logic_for_input_type(:radio)
24
+ it_should_use_the_collection_when_provided(:radio, 'input')
25
+
26
+
27
+ it 'should generate a legend - classified as a label - containing label text for the input' do
28
+ output_buffer.should have_tag('form li fieldset legend.label')
29
+ output_buffer.should have_tag('form li fieldset legend.label', /Author/)
30
+ end
31
+
32
+ it 'should generate an ordered list with a list item for each choice' do
33
+ output_buffer.should have_tag('form li fieldset ol')
34
+ output_buffer.should have_tag('form li fieldset ol li', :count => ::Author.find(:all).size)
35
+ end
36
+
37
+ it 'should have one option with a "checked" attribute' do
38
+ output_buffer.should have_tag('form li input[@checked]', :count => 1)
39
+ end
40
+
41
+ describe "each choice" do
42
+
43
+ it 'should contain a label for the radio input with a nested input and label text' do
44
+ ::Author.find(:all).each do |author|
45
+ output_buffer.should have_tag('form li fieldset ol li label', /#{author.to_label}/)
46
+ output_buffer.should have_tag("form li fieldset ol li label[@for='post_author_id_#{author.id}']")
47
+ end
48
+ end
49
+
50
+ it 'should use values as li.class when value_as_class is true' do
51
+ ::Author.find(:all).each do |author|
52
+ output_buffer.should have_tag("form li fieldset ol li.author_#{author.id} label")
53
+ end
54
+ end
55
+
56
+ it "should have a radio input" do
57
+ ::Author.find(:all).each do |author|
58
+ output_buffer.should have_tag("form li fieldset ol li label input#post_author_id_#{author.id}")
59
+ output_buffer.should have_tag("form li fieldset ol li label input[@type='radio']")
60
+ output_buffer.should have_tag("form li fieldset ol li label input[@value='#{author.id}']")
61
+ output_buffer.should have_tag("form li fieldset ol li label input[@name='post[author_id]']")
62
+ end
63
+ end
64
+
65
+ it "should mark input as checked if it's the the existing choice" do
66
+ @new_post.author_id.should == @bob.id
67
+ @new_post.author.id.should == @bob.id
68
+ @new_post.author.should == @bob
69
+
70
+ semantic_form_for(@new_post) do |builder|
71
+ concat(builder.input(:author, :as => :radio))
72
+ end
73
+
74
+ output_buffer.should have_tag("form li fieldset ol li label input[@checked='checked']")
75
+ end
76
+ end
77
+
78
+ describe 'and no object is given' do
79
+ before(:each) do
80
+ output_buffer.replace ''
81
+ semantic_form_for(:project, :url => 'http://test.host') do |builder|
82
+ concat(builder.input(:author_id, :as => :radio, :collection => ::Author.find(:all)))
83
+ end
84
+ end
85
+
86
+ it 'should generate a fieldset with legend' do
87
+ output_buffer.should have_tag('form li fieldset legend', /Author/)
88
+ end
89
+
90
+ it 'should generate an li tag for each item in the collection' do
91
+ output_buffer.should have_tag('form li fieldset ol li', :count => ::Author.find(:all).size)
92
+ end
93
+
94
+ it 'should generate labels for each item' do
95
+ ::Author.find(:all).each do |author|
96
+ output_buffer.should have_tag('form li fieldset ol li label', /#{author.to_label}/)
97
+ output_buffer.should have_tag("form li fieldset ol li label[@for='project_author_id_#{author.id}']")
98
+ end
99
+ end
100
+
101
+ it 'should generate inputs for each item' do
102
+ ::Author.find(:all).each do |author|
103
+ output_buffer.should have_tag("form li fieldset ol li label input#project_author_id_#{author.id}")
104
+ output_buffer.should have_tag("form li fieldset ol li label input[@type='radio']")
105
+ output_buffer.should have_tag("form li fieldset ol li label input[@value='#{author.id}']")
106
+ output_buffer.should have_tag("form li fieldset ol li label input[@name='project[author_id]']")
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ describe 'when :selected is set' do
113
+ before do
114
+ @output_buffer = ''
115
+ end
116
+
117
+ describe "no selected items" do
118
+ before do
119
+ @new_post.stub!(:author_ids).and_return(nil)
120
+
121
+ semantic_form_for(@new_post) do |builder|
122
+ concat(builder.input(:authors, :as => :radio, :selected => nil))
123
+ end
124
+ end
125
+
126
+ it 'should not have any selected item(s)' do
127
+ output_buffer.should_not have_tag("form li fieldset ol li label input[@checked='checked']")
128
+ end
129
+ end
130
+
131
+ describe "single selected item" do
132
+ before do
133
+ @new_post.stub!(:author_ids).and_return(nil)
134
+
135
+ semantic_form_for(@new_post) do |builder|
136
+ concat(builder.input(:authors, :as => :radio, :selected => @fred.id))
137
+ end
138
+ end
139
+
140
+ it "should have one item selected; the specified one" do
141
+ output_buffer.should have_tag("form li fieldset ol li label input[@type='radio'][@checked='checked']", :count => 1)
142
+ output_buffer.should have_tag("form li fieldset ol li label[@for='post_author_ids_#{@fred.id}']", /fred/i)
143
+ output_buffer.should have_tag("form li fieldset ol li label input[@type='radio'][@checked='checked'][@value='#{@fred.id}']")
144
+ end
145
+ end
146
+
147
+ end
148
+
149
+ end
@@ -0,0 +1,459 @@
1
+ # coding: utf-8
2
+ require File.dirname(__FILE__) + '/../spec_helper'
3
+
4
+ describe 'select input' do
5
+
6
+ include FormtasticSpecHelper
7
+
8
+ before do
9
+ @output_buffer = ''
10
+ mock_everything
11
+ end
12
+
13
+ describe 'explicit values' do
14
+ describe 'using an array of values' do
15
+ before do
16
+ @array_with_values = ["Title A", "Title B", "Title C"]
17
+ @array_with_keys_and_values = [["Title D", 1], ["Title E", 2], ["Title F", 3]]
18
+ semantic_form_for(@new_post) do |builder|
19
+ concat(builder.input(:title, :as => :select, :collection => @array_with_values))
20
+ concat(builder.input(:title, :as => :select, :collection => @array_with_keys_and_values))
21
+ end
22
+ end
23
+
24
+ it 'should have a option for each key and/or value' do
25
+ @array_with_values.each do |v|
26
+ output_buffer.should have_tag("form li select option[@value='#{v}']", /^#{v}$/)
27
+ end
28
+ @array_with_keys_and_values.each do |v|
29
+ output_buffer.should have_tag("form li select option[@value='#{v.second}']", /^#{v.first}$/)
30
+ end
31
+ end
32
+ end
33
+
34
+ describe 'using a range' do
35
+ before do
36
+ @range_with_values = 1..5
37
+ semantic_form_for(@new_post) do |builder|
38
+ concat(builder.input(:title, :as => :select, :collection => @range_with_values))
39
+ end
40
+ end
41
+
42
+ it 'should have an option for each value' do
43
+ @range_with_values.each do |v|
44
+ output_buffer.should have_tag("form li select option[@value='#{v}']", /^#{v}$/)
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ describe 'for a belongs_to association' do
51
+ before do
52
+ semantic_form_for(@new_post) do |builder|
53
+ concat(builder.input(:author, :as => :select))
54
+ end
55
+ end
56
+
57
+ it_should_have_input_wrapper_with_class("select")
58
+ it_should_have_input_wrapper_with_id("post_author_input")
59
+ it_should_have_label_with_text(/Author/)
60
+ it_should_have_label_for('post_author_id')
61
+ it_should_apply_error_logic_for_input_type(:select)
62
+ it_should_call_find_on_association_class_when_no_collection_is_provided(:select)
63
+ it_should_use_the_collection_when_provided(:select, 'option')
64
+
65
+ it 'should have a select inside the wrapper' do
66
+ output_buffer.should have_tag('form li select')
67
+ output_buffer.should have_tag('form li select#post_author_id')
68
+ end
69
+
70
+ it 'should have a valid name' do
71
+ output_buffer.should have_tag("form li select[@name='post[author_id]']")
72
+ output_buffer.should_not have_tag("form li select[@name='post[author_id][]']")
73
+ end
74
+
75
+ it 'should not create a multi-select' do
76
+ output_buffer.should_not have_tag('form li select[@multiple]')
77
+ end
78
+
79
+ it 'should create a select without size' do
80
+ output_buffer.should_not have_tag('form li select[@size]')
81
+ end
82
+
83
+ it 'should have a blank option' do
84
+ output_buffer.should have_tag("form li select option[@value='']")
85
+ end
86
+
87
+ it 'should have a select option for each Author' do
88
+ output_buffer.should have_tag('form li select option', :count => ::Author.find(:all).size + 1)
89
+ ::Author.find(:all).each do |author|
90
+ output_buffer.should have_tag("form li select option[@value='#{author.id}']", /#{author.to_label}/)
91
+ end
92
+ end
93
+
94
+ it 'should have one option with a "selected" attribute' do
95
+ output_buffer.should have_tag('form li select option[@selected]', :count => 1)
96
+ end
97
+
98
+ it 'should not singularize the association name' do
99
+ @new_post.stub!(:author_status).and_return(@bob)
100
+ @new_post.stub!(:author_status_id).and_return(@bob.id)
101
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :integer, :limit => 255))
102
+
103
+ semantic_form_for(@new_post) do |builder|
104
+ concat(builder.input(:author_status, :as => :select))
105
+ end
106
+
107
+ output_buffer.should have_tag('form li select#post_author_status_id')
108
+ end
109
+ end
110
+
111
+ describe "for a belongs_to association with :group_by => :author" do
112
+ it "should call author.posts" do
113
+ [@freds_post].each { |post| post.stub!(:to_label).and_return("Post - #{post.id}") }
114
+ @fred.should_receive(:posts)
115
+
116
+ semantic_form_for(@new_post) do |builder|
117
+ concat(builder.input(:main_post, :as => :select, :group_by => :author ) )
118
+ end
119
+ end
120
+ end
121
+
122
+ describe 'for a belongs_to association with :group_by => :continent' do
123
+ before do
124
+ @authors = [@bob, @fred, @fred, @fred]
125
+ ::Author.stub!(:find).and_return(@authors)
126
+ @continent_names = %w(Europe Africa)
127
+ @continents = (0..1).map { |i| c = ::Continent.new; c.stub!(:id).and_return(100 - i);c }
128
+ @authors[0..1].each_with_index { |author, i| author.stub!(:continent).and_return(@continents[i]) }
129
+ ::Continent.stub!(:reflect_on_all_associations).and_return {|macro| mock('reflection', :klass => Author) if macro == :has_many}
130
+ ::Continent.stub!(:reflect_on_association).and_return {|column_name| mock('reflection', :klass => Author) if column_name == :authors}
131
+ ::Author.stub!(:reflect_on_association).and_return { |column_name| mock('reflection', :options => {}, :klass => Continent, :macro => :belongs_to) if column_name == :continent }
132
+
133
+
134
+ @continents.each_with_index do |continent, i|
135
+ continent.stub!(:to_label).and_return(@continent_names[i])
136
+ continent.stub!(:authors).and_return([@authors[i]])
137
+ end
138
+
139
+ semantic_form_for(@new_post) do |builder|
140
+ concat(builder.input(:author, :as => :select, :group_by => :continent ) )
141
+ concat(builder.input(:author, :as => :select, :group_by => :continent, :group_label_method => :id ) )
142
+ end
143
+ end
144
+
145
+ it_should_have_input_wrapper_with_class("select")
146
+ it_should_have_input_wrapper_with_id("post_author_input")
147
+ it_should_have_label_with_text(/Author/)
148
+ it_should_have_label_for('post_author_id')
149
+
150
+ # TODO, need to find a way to repeat some of the specs and logic from the belongs_to specs without grouping
151
+
152
+ 0.upto(1) do |i|
153
+ it 'should have all option groups and the right values' do
154
+ output_buffer.should have_tag("form li select optgroup[@label='#{@continent_names[i]}']", @authors[i].to_label)
155
+ end
156
+
157
+ it 'should have custom group labels' do
158
+ output_buffer.should have_tag("form li select optgroup[@label='#{@continents[i].id}']", @authors[i].to_label)
159
+ end
160
+ end
161
+
162
+ it 'should have no duplicate groups' do
163
+ output_buffer.should have_tag('form li select optgroup', :count => 4)
164
+ end
165
+
166
+ it 'should sort the groups on the label method' do
167
+ output_buffer.should have_tag("form li select optgroup[@label='Africa']")
168
+ output_buffer.should have_tag("form li select optgroup[@label='99']")
169
+ end
170
+
171
+ it 'should call find with :include for more optimized queries' do
172
+ Author.should_receive(:find).with(:all, :include => :continent)
173
+
174
+ semantic_form_for(@new_post) do |builder|
175
+ concat(builder.input(:author, :as => :select, :group_by => :continent ) )
176
+ end
177
+ end
178
+ end
179
+
180
+ describe 'for a has_many association' do
181
+ before do
182
+ semantic_form_for(@fred) do |builder|
183
+ concat(builder.input(:posts, :as => :select))
184
+ end
185
+ end
186
+
187
+ it_should_have_input_wrapper_with_class("select")
188
+ it_should_have_input_wrapper_with_id("author_posts_input")
189
+ it_should_have_label_with_text(/Post/)
190
+ it_should_have_label_for('author_post_ids')
191
+ it_should_apply_error_logic_for_input_type(:select)
192
+ it_should_call_find_on_association_class_when_no_collection_is_provided(:select)
193
+ it_should_use_the_collection_when_provided(:select, 'option')
194
+
195
+ it 'should have a select inside the wrapper' do
196
+ output_buffer.should have_tag('form li select')
197
+ output_buffer.should have_tag('form li select#author_post_ids')
198
+ end
199
+
200
+ it 'should have a multi-select select' do
201
+ output_buffer.should have_tag('form li select[@multiple="multiple"]')
202
+ end
203
+
204
+ it 'should have a select option for each Post' do
205
+ output_buffer.should have_tag('form li select option', :count => ::Post.find(:all).size)
206
+ ::Post.find(:all).each do |post|
207
+ output_buffer.should have_tag("form li select option[@value='#{post.id}']", /#{post.to_label}/)
208
+ end
209
+ end
210
+
211
+ it 'should not have a blank option' do
212
+ output_buffer.should_not have_tag("form li select option[@value='']")
213
+ end
214
+
215
+ it 'should have one option with a "selected" attribute' do
216
+ output_buffer.should have_tag('form li select option[@selected]', :count => 1)
217
+ end
218
+ end
219
+
220
+ describe 'for a has_and_belongs_to_many association' do
221
+ before do
222
+ semantic_form_for(@freds_post) do |builder|
223
+ concat(builder.input(:authors, :as => :select))
224
+ end
225
+ end
226
+
227
+ it_should_have_input_wrapper_with_class("select")
228
+ it_should_have_input_wrapper_with_id("post_authors_input")
229
+ it_should_have_label_with_text(/Author/)
230
+ it_should_have_label_for('post_author_ids')
231
+ it_should_apply_error_logic_for_input_type(:select)
232
+ it_should_call_find_on_association_class_when_no_collection_is_provided(:select)
233
+ it_should_use_the_collection_when_provided(:select, 'option')
234
+
235
+ it 'should have a select inside the wrapper' do
236
+ output_buffer.should have_tag('form li select')
237
+ output_buffer.should have_tag('form li select#post_author_ids')
238
+ end
239
+
240
+ it 'should have a multi-select select' do
241
+ output_buffer.should have_tag('form li select[@multiple="multiple"]')
242
+ end
243
+
244
+ it 'should have a select option for each Author' do
245
+ output_buffer.should have_tag('form li select option', :count => ::Author.find(:all).size)
246
+ ::Author.find(:all).each do |author|
247
+ output_buffer.should have_tag("form li select option[@value='#{author.id}']", /#{author.to_label}/)
248
+ end
249
+ end
250
+
251
+ it 'should not have a blank option' do
252
+ output_buffer.should_not have_tag("form li select option[@value='']")
253
+ end
254
+
255
+ it 'should have one option with a "selected" attribute' do
256
+ output_buffer.should have_tag('form li select option[@selected]', :count => 1)
257
+ end
258
+ end
259
+
260
+ describe 'when :prompt => "choose something" is set' do
261
+ before do
262
+ @new_post.stub!(:author_id).and_return(nil)
263
+ semantic_form_for(@new_post) do |builder|
264
+ concat(builder.input(:author, :as => :select, :prompt => "choose author"))
265
+ end
266
+ end
267
+
268
+ it 'should have a select with prompt' do
269
+ output_buffer.should have_tag("form li select option[@value='']", /choose author/)
270
+ end
271
+
272
+ it 'should not have a blank select option' do
273
+ output_buffer.should_not have_tag("form li select option[@value='']", "")
274
+ end
275
+ end
276
+
277
+ describe 'when no object is given' do
278
+ before(:each) do
279
+ semantic_form_for(:project, :url => 'http://test.host') do |builder|
280
+ concat(builder.input(:author, :as => :select, :collection => ::Author.find(:all)))
281
+ end
282
+ end
283
+
284
+ it 'should generate label' do
285
+ output_buffer.should have_tag('form li label', /Author/)
286
+ output_buffer.should have_tag("form li label[@for='project_author']")
287
+ end
288
+
289
+ it 'should generate select inputs' do
290
+ output_buffer.should have_tag('form li select#project_author')
291
+ output_buffer.should have_tag('form li select option', :count => ::Author.find(:all).size + 1)
292
+ end
293
+
294
+ it 'should generate an option to each item' do
295
+ ::Author.find(:all).each do |author|
296
+ output_buffer.should have_tag("form li select option[@value='#{author.id}']", /#{author.to_label}/)
297
+ end
298
+ end
299
+ end
300
+
301
+ describe 'when :selected is set' do
302
+ before do
303
+ @output_buffer = ''
304
+ end
305
+
306
+ describe "no selected items" do
307
+ before do
308
+ @new_post.stub!(:author_id).and_return(nil)
309
+ semantic_form_for(@new_post) do |builder|
310
+ concat(builder.input(:author, :as => :select, :selected => nil))
311
+ end
312
+ end
313
+
314
+ it 'should not have any selected item(s)' do
315
+ output_buffer.should_not have_tag("form li select option[@selected='selected']")
316
+ end
317
+ end
318
+
319
+ describe "single selected item" do
320
+ before do
321
+ @new_post.stub!(:author_id).and_return(nil)
322
+ semantic_form_for(@new_post) do |builder|
323
+ concat(builder.input(:author, :as => :select, :selected => @bob.id))
324
+ end
325
+ end
326
+
327
+ it 'should have a selected item; the specified one' do
328
+ output_buffer.should have_tag("form li select option[@selected='selected']", :count => 1)
329
+ output_buffer.should have_tag("form li select option[@selected='selected']", /bob/i)
330
+ output_buffer.should have_tag("form li select option[@selected='selected'][@value='#{@bob.id}']")
331
+ end
332
+ end
333
+
334
+ describe "multiple selected items" do
335
+
336
+ describe "when :multiple => false" do
337
+ before do
338
+ @new_post.stub!(:author_ids).and_return(nil)
339
+
340
+ semantic_form_for(@new_post) do |builder|
341
+ concat(builder.input(:authors, :as => :select, :selected => [@bob.id, @fred.id], :multiple => false))
342
+ end
343
+ end
344
+
345
+ it "should only select the first value" do
346
+ output_buffer.should have_tag("form li select option[@selected='selected']", :count => 1)
347
+ # FIXME: Not supported by Nokogiri.
348
+ # output_buffer.should have_tag("form li select:not([@multiple]) option[@selected='selected']", /bob/i)
349
+ # output_buffer.should have_tag("form li select:not([@multiple]) option[@selected='selected'][@value='#{@bob.id}']")
350
+ end
351
+ end
352
+
353
+ describe "when :multiple => true" do
354
+ before do
355
+ @new_post.stub!(:author_ids).and_return(nil)
356
+
357
+ semantic_form_for(@new_post) do |builder|
358
+ concat(builder.input(:authors, :as => :select, :selected => [@bob.id, @fred.id]))
359
+ end
360
+ end
361
+
362
+ it "should have multiple items selected; the specified ones" do
363
+ output_buffer.should have_tag("form li select option[@selected='selected']", :count => 2)
364
+ output_buffer.should have_tag("form li select[@multiple] option[@selected='selected']", /bob/i)
365
+ output_buffer.should have_tag("form li select[@multiple] option[@selected='selected'][@value='#{@bob.id}']")
366
+ output_buffer.should have_tag("form li select[@multiple] option[@selected='selected']", /fred/i)
367
+ output_buffer.should have_tag("form li select[@multiple] option[@selected='selected'][@value='#{@fred.id}']")
368
+ end
369
+ end
370
+
371
+ end
372
+
373
+ end
374
+
375
+ describe 'boolean select' do
376
+ describe 'default formtastic locale' do
377
+ before do
378
+ # Note: Works, but something like Formtastic.root.join(...) would probably be "safer".
379
+ ::I18n.load_path = [File.join(File.dirname(__FILE__), *%w[.. .. lib locale en.yml])]
380
+ ::I18n.backend.send(:init_translations)
381
+
382
+ semantic_form_for(@new_post) do |builder|
383
+ concat(builder.input(:published, :as => :select))
384
+ end
385
+ end
386
+
387
+ after do
388
+ ::I18n.backend.store_translations :en, {}
389
+ end
390
+
391
+ it 'should render a select with at least options: true/false' do
392
+ output_buffer.should have_tag("form li select option[@value='true']", /^Yes$/)
393
+ output_buffer.should have_tag("form li select option[@value='false']", /^No$/)
394
+ end
395
+ end
396
+
397
+ describe 'custom locale' do
398
+ before do
399
+ @boolean_select_labels = {:yes => 'Yep', :no => 'Nope'}
400
+ ::I18n.backend.store_translations :en, :formtastic => @boolean_select_labels
401
+
402
+ semantic_form_for(@new_post) do |builder|
403
+ concat(builder.input(:published, :as => :select))
404
+ end
405
+ end
406
+
407
+ after do
408
+ ::I18n.backend.store_translations :en, {}
409
+ end
410
+
411
+ it 'should render a select with at least options: true/false' do
412
+ output_buffer.should have_tag("form li select option[@value='true']", /#{@boolean_select_labels[:yes]}/)
413
+ output_buffer.should have_tag("form li select option[@value='false']", /#{@boolean_select_labels[:no]}/)
414
+ end
415
+ end
416
+ end
417
+
418
+ describe "enums" do
419
+ describe ":collection is set" do
420
+ before do
421
+ @output_buffer = ''
422
+ @some_meta_descriptions = ["One", "Two", "Three"]
423
+ @new_post.stub!(:meta_description).any_number_of_times
424
+ end
425
+
426
+ describe ":as is not set" do
427
+ before do
428
+ semantic_form_for(@new_post) do |builder|
429
+ concat(builder.input(:meta_description, :collection => @some_meta_descriptions))
430
+ end
431
+ semantic_form_for(:project, :url => 'http://test.host') do |builder|
432
+ concat(builder.input(:meta_description, :collection => @some_meta_descriptions))
433
+ end
434
+ end
435
+
436
+ it "should render a select field" do
437
+ output_buffer.should have_tag("form li select", :count => 2)
438
+ end
439
+ end
440
+
441
+ describe ":as is set" do
442
+ before do
443
+ # Should not be a case, but just checking :as got highest priority in setting input type.
444
+ semantic_form_for(@new_post) do |builder|
445
+ concat(builder.input(:meta_description, :as => :string, :collection => @some_meta_descriptions))
446
+ end
447
+ semantic_form_for(:project, :url => 'http://test.host') do |builder|
448
+ concat(builder.input(:meta_description, :as => :string, :collection => @some_meta_descriptions))
449
+ end
450
+ end
451
+
452
+ it "should render a text field" do
453
+ output_buffer.should have_tag("form li input[@type='text']", :count => 2)
454
+ end
455
+ end
456
+ end
457
+ end
458
+
459
+ end