forme 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,579 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper.rb')
2
+ require 'date'
3
+
4
+ describe "Forme plain forms" do
5
+ def sel(opts, s)
6
+ opts.map{|o| "<option #{'selected="selected" ' if o == s}value=\"#{o}\">#{sprintf("%02i", o)}</option>"}.join
7
+ end
8
+
9
+ before do
10
+ @f = Forme::Form.new
11
+ end
12
+
13
+ specify "Forme.version should return version string" do
14
+ Forme.version.should =~ /\A\d+\.\d+\.\d+\z/
15
+ end
16
+
17
+ specify "should create a simple input tags" do
18
+ @f.input(:text).to_s.should == '<input type="text"/>'
19
+ @f.input(:radio).to_s.should == '<input type="radio"/>'
20
+ @f.input(:password).to_s.should == '<input type="password"/>'
21
+ @f.input(:checkbox).to_s.should == '<input type="checkbox"/>'
22
+ @f.input(:submit).to_s.should == '<input type="submit"/>'
23
+ end
24
+
25
+ specify "should use :name option as attribute" do
26
+ @f.input(:text, :name=>"foo").to_s.should == '<input name="foo" type="text"/>'
27
+ end
28
+
29
+ specify "should use :id option as attribute" do
30
+ @f.input(:text, :id=>"foo").to_s.should == '<input id="foo" type="text"/>'
31
+ end
32
+
33
+ specify "should use :class option as attribute" do
34
+ @f.input(:text, :class=>"foo").to_s.should == '<input class="foo" type="text"/>'
35
+ end
36
+
37
+ specify "should use :value option as attribute" do
38
+ @f.input(:text, :value=>"foo").to_s.should == '<input type="text" value="foo"/>'
39
+ end
40
+
41
+ specify "should use :placeholder option as attribute" do
42
+ @f.input(:text, :placeholder=>"foo").to_s.should == '<input placeholder="foo" type="text"/>'
43
+ end
44
+
45
+ specify "should allow arbitrary attributes using the :attr option" do
46
+ @f.input(:text, :attr=>{:bar=>"foo"}).to_s.should == '<input bar="foo" type="text"/>'
47
+ end
48
+
49
+ specify "should convert the :data option into attributes" do
50
+ @f.input(:text, :data=>{:bar=>"foo"}).to_s.should == '<input data-bar="foo" type="text"/>'
51
+ end
52
+
53
+ specify "should not have standard options override the :attr option" do
54
+ @f.input(:text, :name=>:bar, :attr=>{:name=>"foo"}).to_s.should == '<input name="foo" type="text"/>'
55
+ end
56
+
57
+ specify "should combine :class standard option with :attr option" do
58
+ @f.input(:text, :class=>:bar, :attr=>{:class=>"foo"}).to_s.should == '<input class="foo bar" type="text"/>'
59
+ end
60
+
61
+ specify "should not have :data options override the :attr option" do
62
+ @f.input(:text, :data=>{:bar=>"baz"}, :attr=>{:"data-bar"=>"foo"}).to_s.should == '<input data-bar="foo" type="text"/>'
63
+ end
64
+
65
+ specify "should create hidden input with value 0 for each checkbox with a name" do
66
+ @f.input(:checkbox, :name=>"foo").to_s.should == '<input name="foo" type="hidden" value="0"/><input name="foo" type="checkbox"/>'
67
+ end
68
+
69
+ specify "should not create hidden input with value 0 for each checkbox with a name if :no_hidden option is used" do
70
+ @f.input(:checkbox, :name=>"foo", :no_hidden=>true).to_s.should == '<input name="foo" type="checkbox"/>'
71
+ end
72
+
73
+ specify "should create hidden input with _hidden appened to id for each checkbox with a name and id" do
74
+ @f.input(:checkbox, :name=>"foo", :id=>"bar").to_s.should == '<input id="bar_hidden" name="foo" type="hidden" value="0"/><input id="bar" name="foo" type="checkbox"/>'
75
+ end
76
+
77
+ specify "should create hidden input with value f for each checkbox with a name and value t" do
78
+ @f.input(:checkbox, :name=>"foo", :value=>"t").to_s.should == '<input name="foo" type="hidden" value="f"/><input name="foo" type="checkbox" value="t"/>'
79
+ end
80
+
81
+ specify "should use :hidden_value option for value of hidden input for checkbox" do
82
+ @f.input(:checkbox, :name=>"foo", :hidden_value=>"no").to_s.should == '<input name="foo" type="hidden" value="no"/><input name="foo" type="checkbox"/>'
83
+ end
84
+
85
+ specify "should handle :checked option" do
86
+ @f.input(:checkbox, :checked=>true).to_s.should == '<input checked="checked" type="checkbox"/>'
87
+ @f.input(:checkbox, :checked=>false).to_s.should == '<input type="checkbox"/>'
88
+ end
89
+
90
+ specify "should create textarea tag" do
91
+ @f.input(:textarea).to_s.should == '<textarea></textarea>'
92
+ end
93
+
94
+ specify "should create select tag" do
95
+ @f.input(:select).to_s.should == '<select></select>'
96
+ end
97
+
98
+ specify "should create date tag" do
99
+ @f.input(:date).to_s.should == '<input type="date"/>'
100
+ end
101
+
102
+ specify "should not error for input type :input" do
103
+ @f.input(:input).to_s.should == '<input type="input"/>'
104
+ end
105
+
106
+ specify "should use multiple select boxes for dates if the :as=>:select option is given" do
107
+ @f.input(:date, :name=>"foo", :id=>"bar", :as=>:select, :value=>Date.new(2011, 6, 5)).to_s.should == %{<select id="bar_year" name="foo[year]">#{sel(1900..2050, 2011)}</select>-<select id="bar_month" name="foo[month]">#{sel(1..12, 6)}</select>-<select id="bar_day" name="foo[day]">#{sel(1..31, 5)}</select>}
108
+ end
109
+
110
+ specify "should have explicit labeler and trtd wrapper work with multiple select boxes for dates" do
111
+ @f.input(:date, :name=>"foo", :id=>"bar", :as=>:select, :value=>Date.new(2011, 6, 5), :wrapper=>:trtd, :labeler=>:explicit, :label=>'Baz').to_s.should == %{<tr><td><label for="bar_year">Baz</label></td><td><select id="bar_year" name="foo[year]">#{sel(1900..2050, 2011)}</select>-<select id="bar_month" name="foo[month]">#{sel(1..12, 6)}</select>-<select id="bar_day" name="foo[day]">#{sel(1..31, 5)}</select></td></tr>}
112
+ end
113
+
114
+ specify "should use multiple select boxes for datetimes if the :as=>:select option is given" do
115
+ @f.input(:datetime, :name=>"foo", :id=>"bar", :as=>:select, :value=>DateTime.new(2011, 6, 5, 4, 3, 2)).to_s.should == %{<select id="bar_year" name="foo[year]">#{sel(1900..2050, 2011)}</select>-<select id="bar_month" name="foo[month]">#{sel(1..12, 6)}</select>-<select id="bar_day" name="foo[day]">#{sel(1..31, 5)}</select> <select id="bar_hour" name="foo[hour]">#{sel(0..23, 4)}</select>:<select id="bar_minute" name="foo[minute]">#{sel(0..59, 3)}</select>:<select id="bar_second" name="foo[second]">#{sel(0..59, 2)}</select>}
116
+ end
117
+
118
+ specify "should create select tag with options" do
119
+ @f.input(:select, :options=>[1, 2, 3], :selected=>2).to_s.should == '<select><option>1</option><option selected="selected">2</option><option>3</option></select>'
120
+ @f.input(:select, :options=>[1, 2, 3], :value=>2).to_s.should == '<select><option>1</option><option selected="selected">2</option><option>3</option></select>'
121
+ end
122
+
123
+ specify "should create select tag with options and values" do
124
+ @f.input(:select, :options=>[[:a, 1], [:b, 2], [:c, 3]], :selected=>2).to_s.should == '<select><option value="1">a</option><option selected="selected" value="2">b</option><option value="3">c</option></select>'
125
+ end
126
+
127
+ specify "should create select tag with options and values with hashes" do
128
+ @f.input(:select, :options=>[[:a, {:foo=>1}], [:b, {:bar=>4, :value=>2}], [:c, {:baz=>3}]], :selected=>2).to_s.should == '<select><option foo="1">a</option><option bar="4" selected="selected" value="2">b</option><option baz="3">c</option></select>'
129
+ end
130
+
131
+ specify "should create select tag with options and values using given method" do
132
+ @f.input(:select, :options=>[[:a, 1], [:b, 2], [:c, 3]], :text_method=>:last, :selected=>2).to_s.should == '<select><option>1</option><option selected="selected">2</option><option>3</option></select>'
133
+ @f.input(:select, :options=>[[:a, 1], [:b, 2], [:c, 3]], :text_method=>:last, :value_method=>:first, :selected=>:b).to_s.should == '<select><option value="a">1</option><option selected="selected" value="b">2</option><option value="c">3</option></select>'
134
+ end
135
+
136
+ specify "should use html attributes specified in options" do
137
+ @f.input(:text, :value=>'foo', :name=>'bar').to_s.should == '<input name="bar" type="text" value="foo"/>'
138
+ @f.input(:textarea, :value=>'foo', :name=>'bar').to_s.should == '<textarea name="bar">foo</textarea>'
139
+ @f.input(:select, :name=>'bar', :options=>[1, 2, 3]).to_s.should == '<select name="bar"><option>1</option><option>2</option><option>3</option></select>'
140
+ end
141
+
142
+ specify "should support :add_blank option for select inputs" do
143
+ @f.input(:select, :options=>[[:b, 2], [:c, 3]], :add_blank=>true, :value=>2).to_s.should == '<select><option value=""></option><option selected="selected" value="2">b</option><option value="3">c</option></select>'
144
+ end
145
+
146
+ specify "should use :add_blank option value as prompt if it is a String" do
147
+ @f.input(:select, :options=>[[:b, 2], [:c, 3]], :add_blank=>"Prompt Here", :value=>2).to_s.should == '<select><option value="">Prompt Here</option><option selected="selected" value="2">b</option><option value="3">c</option></select>'
148
+ end
149
+
150
+ specify "radio and checkbox inputs should handle :checked option" do
151
+ @f.input(:radio, :checked=>true).to_s.should == '<input checked="checked" type="radio"/>'
152
+ @f.input(:radio, :checked=>false).to_s.should == '<input type="radio"/>'
153
+ @f.input(:checkbox, :checked=>true).to_s.should == '<input checked="checked" type="checkbox"/>'
154
+ @f.input(:checkbox, :checked=>false).to_s.should == '<input type="checkbox"/>'
155
+ end
156
+
157
+ specify "inputs should handle :required option" do
158
+ @f.input(:text, :required=>true).to_s.should == '<input required="required" type="text"/>'
159
+ @f.input(:text, :required=>false).to_s.should == '<input type="text"/>'
160
+ end
161
+
162
+ specify "inputs should handle :disabled option" do
163
+ @f.input(:text, :disabled=>true).to_s.should == '<input disabled="disabled" type="text"/>'
164
+ @f.input(:text, :disabled=>false).to_s.should == '<input type="text"/>'
165
+ end
166
+
167
+ specify "inputs should not include options with nil values" do
168
+ @f.input(:text, :name=>nil).to_s.should == '<input type="text"/>'
169
+ @f.input(:textarea, :name=>nil).to_s.should == '<textarea></textarea>'
170
+ end
171
+
172
+ specify "inputs should include options with false values" do
173
+ @f.input(:text, :name=>false).to_s.should == '<input name="false" type="text"/>'
174
+ end
175
+
176
+ specify "should automatically create a label if a :label option is used" do
177
+ @f.input(:text, :label=>'Foo', :value=>'foo').to_s.should == '<label>Foo: <input type="text" value="foo"/></label>'
178
+ end
179
+
180
+ specify "should automatically note the input has errors if :error option is used" do
181
+ @f.input(:text, :error=>'Bad Stuff!', :value=>'foo').to_s.should == '<input class="error" type="text" value="foo"/><span class="error_message">Bad Stuff!</span>'
182
+ end
183
+
184
+ specify "should add to existing :class option if :error option is used" do
185
+ @f.input(:text, :error=>'Bad Stuff!', :class=>'bar', :value=>'foo').to_s.should == '<input class="bar error" type="text" value="foo"/><span class="error_message">Bad Stuff!</span>'
186
+ end
187
+
188
+ specify "#open should return an opening tag" do
189
+ @f.open(:action=>'foo', :method=>'post').to_s.should == '<form action="foo" method="post">'
190
+ end
191
+
192
+ specify "#close should return a closing tag" do
193
+ @f.close.to_s.should == '</form>'
194
+ end
195
+
196
+ specify "#button should return a submit tag" do
197
+ @f.button.to_s.should == '<input type="submit"/>'
198
+ end
199
+
200
+ specify "#button should accept an options hash" do
201
+ @f.button(:name=>'foo', :value=>'bar').to_s.should == '<input name="foo" type="submit" value="bar"/>'
202
+ end
203
+
204
+ specify "#button should accept a string to use as a value" do
205
+ @f.button('foo').to_s.should == '<input type="submit" value="foo"/>'
206
+ end
207
+
208
+ specify "#tag should return a serialized_tag" do
209
+ @f.tag(:textarea).to_s.should == '<textarea></textarea>'
210
+ @f.tag(:textarea, :name=>:foo).to_s.should == '<textarea name="foo"></textarea>'
211
+ @f.tag(:textarea, {:name=>:foo}, :bar).to_s.should == '<textarea name="foo">bar</textarea>'
212
+ end
213
+
214
+ specify "should have an #inputs method for multiple inputs wrapped in a fieldset" do
215
+ @f.inputs([:textarea, :text]).to_s.should == '<fieldset class="inputs"><textarea></textarea><input type="text"/></fieldset>'
216
+ end
217
+
218
+ specify "should have default #inputs method accept an :attr option" do
219
+ @f.inputs([:textarea, :text], :legend=>'Inputs', :attr=>{:class=>'foo', :bar=>'baz'}).to_s.should == '<fieldset bar="baz" class="foo inputs"><legend>Inputs</legend><textarea></textarea><input type="text"/></fieldset>'
220
+ end
221
+
222
+ specify "should have default #inputs method accept a :legend option" do
223
+ @f.inputs([:textarea, :text], :legend=>'Inputs').to_s.should == '<fieldset class="inputs"><legend>Inputs</legend><textarea></textarea><input type="text"/></fieldset>'
224
+ end
225
+
226
+ specify "should have default #inputs method accept a :legend_attr option" do
227
+ @f.inputs([:textarea, :text], :legend=>'Inputs', :legend_attr=>{:class=>'foo'}).to_s.should == '<fieldset class="inputs"><legend class="foo">Inputs</legend><textarea></textarea><input type="text"/></fieldset>'
228
+ end
229
+
230
+ specify "should have an #inputs method take a block and yield to it" do
231
+ @f.inputs{@f.input(:textarea); @f.input(:text)}.to_s.should == '<fieldset class="inputs"><textarea></textarea><input type="text"/></fieldset>'
232
+ end
233
+
234
+ specify "should have an #inputs method work with both args and block" do
235
+ @f.inputs([:textarea]){@f.input(:text)}.to_s.should == '<fieldset class="inputs"><textarea></textarea><input type="text"/></fieldset>'
236
+ end
237
+
238
+ specify "should have an #inputs method support array arguments and creating inputs with the array as argument list" do
239
+ @f.inputs([[:textarea, {:name=>'foo'}], [:text, {:id=>'bar'}]]).to_s.should == '<fieldset class="inputs"><textarea name="foo"></textarea><input id="bar" type="text"/></fieldset>'
240
+ end
241
+
242
+ specify "should escape tag content" do
243
+ @f.tag(:div, {}, ['<p></p>']).to_s.should == '<div>&lt;p&gt;&lt;/p&gt;</div>'
244
+ end
245
+
246
+ specify "should not escape raw tag content" do
247
+ @f.tag(:div, {}, ['<p></p>'.extend(Forme::Raw)]).to_s.should == '<div><p></p></div>'
248
+ end
249
+
250
+ specify "should escape tag content in attribute values" do
251
+ @f.tag(:div, :foo=>'<p></p>').to_s.should == '<div foo="&lt;p&gt;&lt;/p&gt;"></div>'
252
+ end
253
+
254
+ specify "should not escape raw tag content in attribute values" do
255
+ @f.tag(:div, :foo=>'<p></p>'.extend(Forme::Raw)).to_s.should == '<div foo="<p></p>"></div>'
256
+ end
257
+
258
+ specify "should format dates, times, and datetimes in ISO format" do
259
+ @f.tag(:div, :foo=>Date.new(2011, 6, 5)).to_s.should == '<div foo="2011-06-05"></div>'
260
+ @f.tag(:div, :foo=>DateTime.new(2011, 6, 5, 4, 3, 2)).to_s.should == '<div foo="2011-06-05 04:03:02+00:00"></div>'
261
+ @f.tag(:div, :foo=>Time.utc(2011, 6, 5, 4, 3, 2)).to_s.should =~ /<div foo="2011-06-05 04:03:02(GMT|UTC)"><\/div>/
262
+ end
263
+
264
+ specify "inputs should accept a :wrapper option to use a custom wrapper" do
265
+ @f.input(:text, :wrapper=>:li).to_s.should == '<li><input type="text"/></li>'
266
+ end
267
+
268
+ specify "inputs should accept a :wrapper_attr option to use custom wrapper attributes" do
269
+ @f.input(:text, :wrapper=>:li, :wrapper_attr=>{:class=>"foo"}).to_s.should == '<li class="foo"><input type="text"/></li>'
270
+ end
271
+
272
+ specify "inputs should accept a :formatter option to use a custom formatter" do
273
+ @f.input(:text, :formatter=>:readonly, :value=>'1', :label=>'Foo').to_s.should == '<label>Foo: <span>1</span></label>'
274
+ @f.input(:text, :formatter=>:default, :value=>'1', :label=>'Foo').to_s.should == '<label>Foo: <input type="text" value="1"/></label>'
275
+ end
276
+
277
+ specify "inputs should accept a :labeler option to use a custom labeler" do
278
+ @f.input(:textarea, :labeler=>:explicit, :label=>'bar', :id=>:foo).to_s.should == '<label for="foo">bar</label><textarea id="foo"></textarea>'
279
+ end
280
+
281
+ specify "inputs should accept a :error_handler option to use a custom error_handler" do
282
+ @f.input(:textarea, :error_handler=>proc{|t, i| [t, "!!! #{i.opts[:error]}"]}, :error=>'bar', :id=>:foo).to_s.should == '<textarea id="foo"></textarea>!!! bar'
283
+ end
284
+
285
+ specify "#inputs should accept a :inputs_wrapper option to use a custom inputs_wrapper" do
286
+ @f.inputs([:textarea], :inputs_wrapper=>:ol).to_s.should == '<ol><textarea></textarea></ol>'
287
+ end
288
+
289
+ specify "inputs should accept a :wrapper=>nil option to not use a wrapper" do
290
+ Forme::Form.new(:wrapper=>:li).input(:text, :wrapper=>nil).to_s.should == '<input type="text"/>'
291
+ end
292
+
293
+ specify "inputs should accept a :labeler=>nil option to not use a labeler" do
294
+ @f.input(:textarea, :labeler=>nil, :label=>'bar', :id=>:foo).to_s.should == '<textarea id="foo"></textarea>'
295
+ end
296
+
297
+ specify "inputs should accept a :error_handler=>nil option to not use an error_handler" do
298
+ @f.input(:textarea, :error_handler=>nil, :error=>'bar', :id=>:foo).to_s.should == '<textarea id="foo"></textarea>'
299
+ end
300
+
301
+ specify "#inputs should accept a :inputs_wrapper=>nil option to not use an inputs_wrapper" do
302
+ @f.form{|f| f.inputs([:textarea], :inputs_wrapper=>nil)}.to_s.should == '<form><textarea></textarea></form>'
303
+ end
304
+
305
+ specify "invalid custom transformers should raise an Error" do
306
+ proc{Forme::Form.new(:wrapper=>Object.new)}.should raise_error(Forme::Error)
307
+ proc{@f.input(:textarea, :wrapper=>Object.new).to_s}.should raise_error(Forme::Error)
308
+ proc{@f.input(:textarea, :formatter=>nil).to_s}.should raise_error(Forme::Error)
309
+ end
310
+ end
311
+
312
+ describe "Forme custom" do
313
+ specify "formatters can be specified as a proc" do
314
+ Forme::Form.new(:formatter=>proc{|i| i.form._tag(:textarea, i.opts.map{|k,v| [v.upcase, k.to_s.downcase]})}).input(:text, :name=>'foo').to_s.should == '<textarea FOO="name"></textarea>'
315
+ end
316
+
317
+ specify "serializers can be specified as a proc" do
318
+ Forme::Form.new(:serializer=>proc{|t| "#{t.type} = #{t.opts.inspect}"}).input(:textarea, :name=>'foo').to_s.should == 'textarea = {:name=>"foo"}'
319
+ end
320
+
321
+ specify "labelers can be specified as a proc" do
322
+ Forme::Form.new(:labeler=>proc{|t, i| ["#{i.opts[:label]}: ", t]}).input(:textarea, :name=>'foo', :label=>'bar').to_s.should == 'bar: <textarea name="foo"></textarea>'
323
+ end
324
+
325
+ specify "error_handlers can be specified as a proc" do
326
+ Forme::Form.new(:error_handler=>proc{|t, i| [t, "!!! #{i.opts[:error]}"]}).input(:textarea, :name=>'foo', :error=>'bar').to_s.should == '<textarea name="foo"></textarea>!!! bar'
327
+ end
328
+
329
+ specify "wrappers can be specified as a proc" do
330
+ Forme::Form.new(:wrapper=>proc{|t, i| t.tag(:div, {:bar=>i.opts[:name]}, t)}).input(:textarea, :name=>'foo').to_s.should == '<div bar="foo"><textarea name="foo"></textarea></div>'
331
+ end
332
+
333
+ specify "inputs_wrappers can be specified as a proc" do
334
+ Forme::Form.new(:inputs_wrapper=>proc{|f, opts, &block| f.tag(:div, &block)}).inputs([:textarea]).to_s.should == '<div><textarea></textarea></div>'
335
+ end
336
+ end
337
+
338
+ describe "Forme built-in custom" do
339
+ specify "transformers should raise if the there is no matching transformer" do
340
+ proc{Forme::Form.new(:formatter=>:foo)}.should raise_error(Forme::Error)
341
+ end
342
+
343
+ specify "formatter: disabled disables all inputs unless :disabled=>false option" do
344
+ Forme::Form.new(:formatter=>:disabled).input(:textarea).to_s.should == '<textarea disabled="disabled"></textarea>'
345
+ Forme::Form.new(:formatter=>:disabled).input(:textarea, :disabled=>false).to_s.should == '<textarea></textarea>'
346
+ end
347
+
348
+ specify "formatter: readonly uses spans for most input fields and disables radio/checkbox fields" do
349
+ Forme::Form.new(:formatter=>:readonly).input(:textarea, :label=>"Foo", :value=>"Bar").to_s.should == "<label>Foo: <span>Bar</span></label>"
350
+ Forme::Form.new(:formatter=>:readonly).input(:text, :label=>"Foo", :value=>"Bar").to_s.should == "<label>Foo: <span>Bar</span></label>"
351
+ Forme::Form.new(:formatter=>:readonly).input(:radio, :label=>"Foo", :value=>"Bar").to_s.should == "<label><input disabled=\"disabled\" type=\"radio\" value=\"Bar\"/> Foo</label>"
352
+ Forme::Form.new(:formatter=>:readonly).input(:radio, :label=>"Foo", :value=>"Bar", :checked=>true).to_s.should == "<label><input checked=\"checked\" disabled=\"disabled\" type=\"radio\" value=\"Bar\"/> Foo</label>"
353
+ Forme::Form.new(:formatter=>:readonly).input(:checkbox, :label=>"Foo", :value=>"Bar").to_s.should == "<label><input disabled=\"disabled\" type=\"checkbox\" value=\"Bar\"/> Foo</label>"
354
+ Forme::Form.new(:formatter=>:readonly).input(:checkbox, :label=>"Foo", :value=>"Bar", :checked=>true).to_s.should == "<label><input checked=\"checked\" disabled=\"disabled\" type=\"checkbox\" value=\"Bar\"/> Foo</label>"
355
+ Forme::Form.new(:formatter=>:readonly).input(:select, :label=>"Foo", :options=>[1, 2, 3], :value=>2).to_s.should == "<label>Foo: <span>2</span></label>"
356
+ end
357
+
358
+ specify "labeler: explicit uses an explicit label with for attribute" do
359
+ Forme::Form.new(:labeler=>:explicit).input(:textarea, :id=>'foo', :label=>'bar').to_s.should == '<label for="foo">bar</label><textarea id="foo"></textarea>'
360
+ end
361
+
362
+ specify "labeler: explicit should handle tags with errors" do
363
+ Forme::Form.new(:labeler=>:explicit).input(:text, :error=>'Bad Stuff!', :value=>'f', :id=>'foo', :label=>'bar').to_s.should == '<label for="foo">bar</label><input class="error" id="foo" type="text" value="f"/><span class="error_message">Bad Stuff!</span>'
364
+ end
365
+
366
+ specify "wrapper: li wraps tag in an li" do
367
+ Forme::Form.new(:wrapper=>:li).input(:textarea, :id=>'foo').to_s.should == '<li><textarea id="foo"></textarea></li>'
368
+ end
369
+
370
+ specify "wrapper: p wraps tag in an p" do
371
+ Forme::Form.new(:wrapper=>:p).input(:textarea, :id=>'foo').to_s.should == '<p><textarea id="foo"></textarea></p>'
372
+ end
373
+
374
+ specify "wrapper: div wraps tag in an div" do
375
+ Forme::Form.new(:wrapper=>:div).input(:textarea, :id=>'foo').to_s.should == '<div><textarea id="foo"></textarea></div>'
376
+ end
377
+
378
+ specify "wrapper: span wraps tag in an span" do
379
+ Forme::Form.new(:wrapper=>:span).input(:textarea, :id=>'foo').to_s.should == '<span><textarea id="foo"></textarea></span>'
380
+ end
381
+
382
+ specify "wrapper: trtd wraps tag in an tr/td" do
383
+ Forme::Form.new(:wrapper=>:trtd).input(:textarea, :id=>'foo').to_s.should == '<tr><td><textarea id="foo"></textarea></td></tr>'
384
+ end
385
+
386
+ specify "wrapper: trtd supports multiple tags in separate tds" do
387
+ Forme::Form.new(:wrapper=>:trtd, :labeler=>:explicit).input(:textarea, :id=>'foo', :label=>'Foo').to_s.should == '<tr><td><label for="foo">Foo</label></td><td><textarea id="foo"></textarea></td></tr>'
388
+ end
389
+
390
+ specify "wrapper: trtd should use at most 2 td tags" do
391
+ Forme::Form.new(:wrapper=>:trtd, :labeler=>:explicit).input(:textarea, :id=>'foo', :label=>'Foo', :error=>'Bar').to_s.should == '<tr><td><label for="foo">Foo</label></td><td><textarea class="error" id="foo"></textarea><span class="error_message">Bar</span></td></tr>'
392
+ end
393
+
394
+ specify "inputs_wrapper: ol wraps tags in an ol" do
395
+ Forme::Form.new(:inputs_wrapper=>:ol, :wrapper=>:li).inputs([:textarea]).to_s.should == '<ol><li><textarea></textarea></li></ol>'
396
+ end
397
+
398
+ specify "inputs_wrapper: fieldset_ol wraps tags in a fieldset and an ol" do
399
+ Forme::Form.new(:inputs_wrapper=>:fieldset_ol, :wrapper=>:li).inputs([:textarea]).to_s.should == '<fieldset class="inputs"><ol><li><textarea></textarea></li></ol></fieldset>'
400
+ end
401
+
402
+ specify "inputs_wrapper: fieldset_ol supports a :legend option" do
403
+ Forme.form({}, :inputs_wrapper=>:fieldset_ol, :wrapper=>:li, :legend=>'Foo', :inputs=>[:textarea]).to_s.should == '<form><fieldset class="inputs"><legend>Foo</legend><ol><li><textarea></textarea></li></ol></fieldset></form>'
404
+ end
405
+
406
+ specify "inputs_wrapper: div wraps tags in a div" do
407
+ Forme::Form.new(:inputs_wrapper=>:div, :wrapper=>:span).inputs([:textarea]).to_s.should == '<div><span><textarea></textarea></span></div>'
408
+ end
409
+
410
+ specify "inputs_wrapper: table wraps tags in an table" do
411
+ Forme::Form.new(:inputs_wrapper=>:table, :wrapper=>:trtd).inputs([:textarea]).to_s.should == '<table><tr><td><textarea></textarea></td></tr></table>'
412
+ end
413
+
414
+ specify "serializer: html_usa formats dates and datetimes in American format without timezones" do
415
+ Forme::Form.new(:serializer=>:html_usa).tag(:div, :foo=>Date.new(2011, 6, 5)).to_s.should == '<div foo="06/05/2011"></div>'
416
+ Forme::Form.new(:serializer=>:html_usa).tag(:div, :foo=>DateTime.new(2011, 6, 5, 16, 3, 2)).to_s.should == '<div foo="06/05/2011 04:03:02PM"></div>'
417
+ Forme::Form.new(:serializer=>:html_usa).tag(:div, :foo=>Time.utc(2011, 6, 5, 4, 3, 2)).to_s.should == '<div foo="06/05/2011 04:03:02AM"></div>'
418
+ end
419
+
420
+ specify "serializer: text uses plain text output instead of html" do
421
+ Forme::Form.new(:serializer=>:text).input(:textarea, :label=>"Foo", :value=>"Bar").to_s.should == "Foo: Bar\n\n"
422
+ Forme::Form.new(:serializer=>:text).input(:text, :label=>"Foo", :value=>"Bar").to_s.should == "Foo: Bar\n\n"
423
+ Forme::Form.new(:serializer=>:text).input(:radio, :label=>"Foo", :value=>"Bar").to_s.should == "___ Foo\n"
424
+ Forme::Form.new(:serializer=>:text).input(:radio, :label=>"Foo", :value=>"Bar", :checked=>true).to_s.should == "_X_ Foo\n"
425
+ Forme::Form.new(:serializer=>:text).input(:checkbox, :label=>"Foo", :value=>"Bar").to_s.should == "___ Foo\n"
426
+ Forme::Form.new(:serializer=>:text).input(:checkbox, :label=>"Foo", :value=>"Bar", :checked=>true).to_s.should == "_X_ Foo\n"
427
+ Forme::Form.new(:serializer=>:text).input(:select, :label=>"Foo", :options=>[1, 2, 3], :value=>2).to_s.should == "Foo: \n___ 1\n_X_ 2\n___ 3\n\n"
428
+ Forme::Form.new(:serializer=>:text).input(:password, :label=>"Pass").to_s.should == "Pass: ********\n\n"
429
+ Forme::Form.new(:serializer=>:text).button().to_s.should == ""
430
+ Forme::Form.new(:serializer=>:text).inputs([[:textarea, {:label=>"Foo", :value=>"Bar"}]], :legend=>'Baz').to_s.should == "Baz\n---\nFoo: Bar\n\n"
431
+ Forme::Form.new(:serializer=>:text).tag(:p){|f| f.input(:textarea, :label=>"Foo", :value=>"Bar")}.to_s.should == "Foo: Bar\n\n"
432
+ end
433
+ end
434
+
435
+ describe "Forme registering custom transformers" do
436
+ specify "should have #register_transformer register a transformer object for later use" do
437
+ Forme.register_transformer(:wrapper, :div, proc{|t, i| t.tag(:div, {}, [t])})
438
+ Forme::Form.new(:wrapper=>:div).input(:textarea).to_s.should == '<div><textarea></textarea></div>'
439
+ end
440
+
441
+ specify "should have #register_transformer register a transformer block for later use" do
442
+ Forme.register_transformer(:wrapper, :div1){|t, i| t.tag(:div1, {}, [t])}
443
+ Forme::Form.new(:wrapper=>:div1).input(:textarea).to_s.should == '<div1><textarea></textarea></div1>'
444
+ end
445
+
446
+ specify "should have #register_transformer raise an error if given a block and an object" do
447
+ proc do
448
+ Forme.register_transformer(:wrapper, :div1, proc{|t, i| t}){|t| t.tag(:div1, {}, [t])}
449
+ end.should raise_error(Forme::Error)
450
+ end
451
+ end
452
+
453
+ describe "Forme configurations" do
454
+ after do
455
+ Forme.default_config = :default
456
+ end
457
+
458
+ specify "config: :formastic uses fieldset_ol inputs_wrapper and li wrapper, and explicit labeler" do
459
+ Forme::Form.new(:config=>:formtastic).inputs([[:textarea, {:id=>:foo, :label=>'Foo'}]]).to_s.should == '<fieldset class="inputs"><ol><li><label for="foo">Foo</label><textarea id="foo"></textarea></li></ol></fieldset>'
460
+ end
461
+
462
+ specify "should be able to set a default configuration with Forme.default_config=" do
463
+ Forme.default_config = :formtastic
464
+ Forme::Form.new.inputs([[:textarea, {:id=>:foo, :label=>'Foo'}]]).to_s.should == '<fieldset class="inputs"><ol><li><label for="foo">Foo</label><textarea id="foo"></textarea></li></ol></fieldset>'
465
+ end
466
+
467
+ specify "should have #register_config register a configuration for later use" do
468
+ Forme.register_config(:foo, :wrapper=>:li, :labeler=>:explicit)
469
+ Forme::Form.new(:config=>:foo).input(:textarea, :id=>:foo, :label=>'Foo').to_s.should == '<li><label for="foo">Foo</label><textarea id="foo"></textarea></li>'
470
+ end
471
+
472
+ specify "should have #register_config support a :base option to base it on an existing config" do
473
+ Forme.register_config(:foo2, :labeler=>:default, :base=>:formtastic)
474
+ Forme::Form.new(:config=>:foo2).inputs([[:textarea, {:id=>:foo, :label=>'Foo'}]]).to_s.should == '<fieldset class="inputs"><ol><li><label>Foo: <textarea id="foo"></textarea></label></li></ol></fieldset>'
475
+ end
476
+
477
+ end
478
+
479
+ describe "Forme object forms" do
480
+ specify "should handle a simple case" do
481
+ obj = Class.new{def forme_input(form, field, opts) form._input(:text, :name=>"obj[#{field}]", :id=>"obj_#{field}", :value=>"#{field}_foo") end}.new
482
+ Forme::Form.new(obj).input(:field).to_s.should == '<input id="obj_field" name="obj[field]" type="text" value="field_foo"/>'
483
+ end
484
+
485
+ specify "should handle more complex case with multiple different types and opts" do
486
+ obj = Class.new do
487
+ def self.name() "Foo" end
488
+
489
+ attr_reader :x, :y
490
+
491
+ def initialize(x, y)
492
+ @x, @y = x, y
493
+ end
494
+ def forme_input(form, field, opts={})
495
+ t = opts[:type]
496
+ t ||= (field == :x ? :textarea : :text)
497
+ s = field.to_s
498
+ form._input(t, {:label=>s.upcase, :name=>"foo[#{s}]", :id=>"foo_#{s}", :value=>send(field)}.merge!(opts))
499
+ end
500
+ end.new('&foo', 3)
501
+ f = Forme::Form.new(obj)
502
+ f.input(:x).to_s.should == '<label>X: <textarea id="foo_x" name="foo[x]">&amp;foo</textarea></label>'
503
+ f.input(:y, :attr=>{:brain=>'no'}).to_s.should == '<label>Y: <input brain="no" id="foo_y" name="foo[y]" type="text" value="3"/></label>'
504
+ end
505
+
506
+ specify "should handle case where obj doesn't respond to forme_input" do
507
+ Forme::Form.new([:foo]).input(:first).to_s.should == '<input id="first" name="first" type="text" value="foo"/>'
508
+ obj = Class.new{attr_accessor :foo}.new
509
+ obj.foo = 'bar'
510
+ Forme::Form.new(obj).input(:foo).to_s.should == '<input id="foo" name="foo" type="text" value="bar"/>'
511
+ end
512
+
513
+ specify "should respect opts hash when obj doesn't respond to forme_input" do
514
+ Forme::Form.new([:foo]).input(:first, :name=>'bar').to_s.should == '<input id="first" name="bar" type="text" value="foo"/>'
515
+ Forme::Form.new([:foo]).input(:first, :id=>'bar').to_s.should == '<input id="bar" name="first" type="text" value="foo"/>'
516
+ Forme::Form.new([:foo]).input(:first, :value=>'bar').to_s.should == '<input id="first" name="first" type="text" value="bar"/>'
517
+ Forme::Form.new([:foo]).input(:first, :attr=>{:x=>'bar'}).to_s.should == '<input id="first" name="first" type="text" value="foo" x="bar"/>'
518
+ end
519
+
520
+ specify "should handle obj passed in via :obj hash key" do
521
+ Forme::Form.new(:obj=>[:foo]).input(:first).to_s.should == '<input id="first" name="first" type="text" value="foo"/>'
522
+ end
523
+
524
+ specify "should be able to turn off obj handling per input using :obj=>nil option" do
525
+ Forme::Form.new([:foo]).input(:checkbox, :name=>"foo", :hidden_value=>"no", :obj=>nil).to_s.should == '<input name="foo" type="hidden" value="no"/><input name="foo" type="checkbox"/>'
526
+ end
527
+ end
528
+
529
+ describe "Forme.form DSL" do
530
+ specify "should return a form tag" do
531
+ Forme.form.to_s.should == '<form></form>'
532
+ end
533
+
534
+ specify "should yield a Form object to the block" do
535
+ Forme.form{|f| f.should be_a_kind_of(Forme::Form)}
536
+ end
537
+
538
+ specify "should have inputs called instead the block be added to the existing form" do
539
+ Forme.form{|f| f.input(:text)}.to_s.should == '<form><input type="text"/></form>'
540
+ end
541
+
542
+ specify "should be able to nest inputs inside tags" do
543
+ Forme.form{|f| f.tag(:div){f.input(:text)}}.to_s.should == '<form><div><input type="text"/></div></form>'
544
+ Forme.form{|f| f.tag(:div){f.tag(:fieldset){f.input(:text)}}}.to_s.should == '<form><div><fieldset><input type="text"/></fieldset></div></form>'
545
+ Forme.form{|f| f.tag(:div){f.tag(:fieldset){f.tag(:span){f.input(:text)}}}}.to_s.should == '<form><div><fieldset><span><input type="text"/></span></fieldset></div></form>'
546
+ Forme.form{|f| f.tag(:div){f.input(:text)}; f.input(:radio)}.to_s.should == '<form><div><input type="text"/></div><input type="radio"/></form>'
547
+ Forme.form{|f| f.tag(:div){f.tag(:fieldset){f.input(:text); f.input(:radio)}; f.input(:checkbox)}}.to_s.should == '<form><div><fieldset><input type="text"/><input type="radio"/></fieldset><input type="checkbox"/></div></form>'
548
+ end
549
+
550
+ specify "should handle an :inputs option to automatically create inputs" do
551
+ Forme.form({}, :inputs=>[:text, :textarea]).to_s.should == '<form><fieldset class="inputs"><input type="text"/><textarea></textarea></fieldset></form>'
552
+ end
553
+
554
+ specify "should handle a :legend option if inputs is used" do
555
+ Forme.form({}, :inputs=>[:text, :textarea], :legend=>'Foo').to_s.should == '<form><fieldset class="inputs"><legend>Foo</legend><input type="text"/><textarea></textarea></fieldset></form>'
556
+ end
557
+
558
+ specify "should still work with a block if :inputs is used" do
559
+ Forme.form({}, :inputs=>[:text]){|f| f.input(:textarea)}.to_s.should == '<form><fieldset class="inputs"><input type="text"/></fieldset><textarea></textarea></form>'
560
+ end
561
+
562
+ specify "should handle an :button option to automatically create a button" do
563
+ Forme.form({}, :button=>'Foo').to_s.should == '<form><input type="submit" value="Foo"/></form>'
564
+ end
565
+
566
+ specify "should allow :button option value to be a hash" do
567
+ Forme.form({}, :button=>{:value=>'Foo', :name=>'bar'}).to_s.should == '<form><input name="bar" type="submit" value="Foo"/></form>'
568
+ end
569
+
570
+ specify "should handle an :button option work with a block" do
571
+ Forme.form({}, :button=>'Foo'){|f| f.input(:textarea)}.to_s.should == '<form><textarea></textarea><input type="submit" value="Foo"/></form>'
572
+ end
573
+
574
+ specify "should have an :button and :inputs option work together" do
575
+ Forme.form({}, :inputs=>[:text, :textarea], :button=>'Foo').to_s.should == '<form><fieldset class="inputs"><input type="text"/><textarea></textarea></fieldset><input type="submit" value="Foo"/></form>'
576
+ end
577
+
578
+ end
579
+