hanami 2.0.3 → 2.1.0.beta1

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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +25 -9
  5. data/hanami.gemspec +2 -2
  6. data/lib/hanami/config/actions.rb +0 -4
  7. data/lib/hanami/config/views.rb +0 -4
  8. data/lib/hanami/config.rb +54 -0
  9. data/lib/hanami/extensions/action/slice_configured_action.rb +15 -7
  10. data/lib/hanami/extensions/action.rb +4 -4
  11. data/lib/hanami/extensions/router/errors.rb +58 -0
  12. data/lib/hanami/extensions/view/context.rb +129 -60
  13. data/lib/hanami/extensions/view/part.rb +26 -0
  14. data/lib/hanami/extensions/view/scope.rb +26 -0
  15. data/lib/hanami/extensions/view/slice_configured_context.rb +0 -2
  16. data/lib/hanami/extensions/view/slice_configured_helpers.rb +44 -0
  17. data/lib/hanami/extensions/view/slice_configured_view.rb +106 -21
  18. data/lib/hanami/extensions/view/standard_helpers.rb +14 -0
  19. data/lib/hanami/extensions.rb +10 -3
  20. data/lib/hanami/helpers/form_helper/form_builder.rb +1391 -0
  21. data/lib/hanami/helpers/form_helper/values.rb +75 -0
  22. data/lib/hanami/helpers/form_helper.rb +213 -0
  23. data/lib/hanami/middleware/public_errors_app.rb +75 -0
  24. data/lib/hanami/middleware/render_errors.rb +93 -0
  25. data/lib/hanami/slice.rb +27 -2
  26. data/lib/hanami/slice_configurable.rb +3 -2
  27. data/lib/hanami/version.rb +1 -1
  28. data/lib/hanami/web/rack_logger.rb +1 -1
  29. data/lib/hanami.rb +1 -1
  30. data/spec/integration/action/view_rendering/view_context_spec.rb +221 -0
  31. data/spec/integration/action/view_rendering_spec.rb +0 -18
  32. data/spec/integration/rack_app/middleware_spec.rb +23 -23
  33. data/spec/integration/rack_app/rack_app_spec.rb +5 -1
  34. data/spec/integration/view/config/default_context_spec.rb +149 -0
  35. data/spec/integration/view/{inflector_spec.rb → config/inflector_spec.rb} +1 -1
  36. data/spec/integration/view/config/part_class_spec.rb +147 -0
  37. data/spec/integration/view/config/part_namespace_spec.rb +103 -0
  38. data/spec/integration/view/config/paths_spec.rb +119 -0
  39. data/spec/integration/view/config/scope_class_spec.rb +147 -0
  40. data/spec/integration/view/config/scope_namespace_spec.rb +103 -0
  41. data/spec/integration/view/config/template_spec.rb +38 -0
  42. data/spec/integration/view/context/request_spec.rb +3 -7
  43. data/spec/integration/view/helpers/form_helper_spec.rb +174 -0
  44. data/spec/integration/view/helpers/part_helpers_spec.rb +124 -0
  45. data/spec/integration/view/helpers/scope_helpers_spec.rb +84 -0
  46. data/spec/integration/view/helpers/user_defined_helpers/part_helpers_spec.rb +162 -0
  47. data/spec/integration/view/helpers/user_defined_helpers/scope_helpers_spec.rb +119 -0
  48. data/spec/integration/view/slice_configuration_spec.rb +9 -9
  49. data/spec/integration/web/render_detailed_errors_spec.rb +90 -0
  50. data/spec/integration/web/render_errors_spec.rb +240 -0
  51. data/spec/spec_helper.rb +1 -1
  52. data/spec/support/matchers.rb +32 -0
  53. data/spec/unit/hanami/config/actions/default_values_spec.rb +0 -4
  54. data/spec/unit/hanami/config/render_detailed_errors_spec.rb +25 -0
  55. data/spec/unit/hanami/config/render_errors_spec.rb +25 -0
  56. data/spec/unit/hanami/config/views_spec.rb +0 -18
  57. data/spec/unit/hanami/extensions/view/context_spec.rb +59 -0
  58. data/spec/unit/hanami/helpers/form_helper_spec.rb +2826 -0
  59. data/spec/unit/hanami/router/errors/not_allowed_error_spec.rb +27 -0
  60. data/spec/unit/hanami/router/errors/not_found_error_spec.rb +22 -0
  61. data/spec/unit/hanami/slice_configurable_spec.rb +18 -0
  62. data/spec/unit/hanami/version_spec.rb +1 -1
  63. data/spec/unit/hanami/web/rack_logger_spec.rb +1 -1
  64. metadata +65 -33
  65. data/spec/integration/action/view_integration_spec.rb +0 -165
  66. data/spec/integration/view/part_namespace_spec.rb +0 -96
  67. data/spec/integration/view/path_spec.rb +0 -56
  68. data/spec/integration/view/template_spec.rb +0 -68
  69. data/spec/isolation/hanami/application/already_configured_spec.rb +0 -19
  70. data/spec/isolation/hanami/application/inherit_anonymous_class_spec.rb +0 -10
  71. data/spec/isolation/hanami/application/inherit_concrete_class_spec.rb +0 -14
  72. data/spec/isolation/hanami/application/not_configured_spec.rb +0 -9
  73. data/spec/isolation/hanami/application/routes/configured_spec.rb +0 -44
  74. data/spec/isolation/hanami/application/routes/not_configured_spec.rb +0 -16
  75. data/spec/isolation/hanami/boot/success_spec.rb +0 -50
@@ -0,0 +1,2826 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/helpers/form_helper"
4
+ require "hanami/view/erb/template"
5
+
6
+ RSpec.describe Hanami::Helpers::FormHelper do
7
+ subject(:obj) {
8
+ Class.new {
9
+ include Hanami::Helpers::FormHelper
10
+
11
+ attr_reader :_context
12
+
13
+ def initialize(context)
14
+ @_context = context
15
+ end
16
+ }.new(context)
17
+ }
18
+
19
+ let(:context) {
20
+ Hanami::View::Context.new(request: request, inflector: Dry::Inflector.new)
21
+ }
22
+
23
+ let(:request) {
24
+ Hanami::Action::Request.new(env: rack_request, params: params, session_enabled: true)
25
+ }
26
+
27
+ let(:rack_request) {
28
+ Rack::MockRequest.env_for("http://example.com/")
29
+ }
30
+
31
+ let(:params) { {} }
32
+
33
+ def form_for(...)
34
+ obj.instance_eval { form_for(...) }
35
+ end
36
+
37
+ def h(&block)
38
+ obj.instance_eval(&block)
39
+ end
40
+
41
+ def render(erb)
42
+ Hanami::View::ERB::Template.new { erb }.render(obj)
43
+ end
44
+
45
+ describe "#form_for" do
46
+ it "renders" do
47
+ html = form_for("/books")
48
+ expect(html).to eq %(<form action="/books" accept-charset="utf-8" method="POST"></form>)
49
+ end
50
+
51
+ it "allows to assign 'id' attribute" do
52
+ html = form_for("/books", id: "book-form")
53
+
54
+ expect(html).to eq %(<form action="/books" id="book-form" accept-charset="utf-8" method="POST"></form>)
55
+ end
56
+
57
+ it "allows to override 'method' attribute ('get')" do
58
+ html = form_for("/books", method: "get")
59
+ expect(html).to eq %(<form action="/books" method="GET" accept-charset="utf-8"></form>)
60
+ end
61
+
62
+ it "allows to override 'method' attribute (:get)" do
63
+ html = form_for("/books", method: :get)
64
+ expect(html).to eq %(<form action="/books" method="GET" accept-charset="utf-8"></form>)
65
+ end
66
+
67
+ it "allows to override 'method' attribute ('GET')" do
68
+ html = form_for("/books", method: "GET")
69
+ expect(html).to eq %(<form action="/books" method="GET" accept-charset="utf-8"></form>)
70
+ end
71
+
72
+ %i[patch put delete].each do |verb|
73
+ it "allows to override 'method' attribute (#{verb})" do
74
+ html = form_for("/books", method: verb) do |f|
75
+ f.text_field "book.title"
76
+ end
77
+
78
+ expect(html).to eq %(<form action="/books" method="POST" accept-charset="utf-8"><input type="hidden" name="_method" value="#{verb.to_s.upcase}"><input type="text" name="book[title]" id="book-title" value=""></form>)
79
+ end
80
+ end
81
+
82
+ it "allows to specify HTML attributes" do
83
+ html = form_for("/books", class: "form-horizonal")
84
+ expect(html).to eq %(<form action="/books" class="form-horizonal" accept-charset="utf-8" method="POST"></form>)
85
+ end
86
+
87
+ context "input name" do
88
+ it "sets a base name" do
89
+ expected_html = <<~HTML
90
+ <form action="/books" accept-charset="utf-8" method="POST">
91
+ <input type="text" name="book[author][avatar][url]" id="book-author-avatar-url" value="">
92
+ </form>
93
+ HTML
94
+
95
+ html = form_for("book", "/books") do |f|
96
+ f.text_field("author.avatar.url")
97
+ end
98
+
99
+ expect(html).to eq_html expected_html
100
+
101
+ html = form_for("book", "/books") do |f|
102
+ f.fields_for("author.avatar") do |fa|
103
+ fa.text_field("url")
104
+ end
105
+ end
106
+
107
+ expect(html).to eq_html expected_html
108
+ end
109
+
110
+ it "renders nested field names" do
111
+ html = form_for("/books") do |f|
112
+ f.text_field "book.author.avatar.url"
113
+ end
114
+
115
+ expect(html).to include %(<input type="text" name="book[author][avatar][url]" id="book-author-avatar-url" value="">)
116
+ end
117
+
118
+ context "using values from scope locals" do
119
+ let(:values) { {book: double("book", author: double("author", avatar: double("avatar", url: val)))} }
120
+ let(:val) { "https://hanami.test/avatar.png" }
121
+
122
+ before do
123
+ # TODO: Maybe our `obj` should actually be a real Scope subclass
124
+ values = self.values
125
+ obj.define_singleton_method(:_locals) { values }
126
+ end
127
+
128
+ it "renders with value" do
129
+ html = form_for("/books") do |f|
130
+ f.text_field "book.author.avatar.url"
131
+ end
132
+
133
+ expect(html).to include %(<input type="text" name="book[author][avatar][url]" id="book-author-avatar-url" value="#{val}">)
134
+ end
135
+ end
136
+
137
+ context "with explicit values given" do
138
+ let(:values) { {book: double("book", author: double("author", avatar: double("avatar", url: val)))} }
139
+ let(:val) { "https://hanami.test/avatar.png" }
140
+
141
+ it "renders with value" do
142
+ html = form_for("/books", values: values) do |f|
143
+ f.text_field "book.author.avatar.url"
144
+ end
145
+
146
+ expect(html).to include %(<input type="text" name="book[author][avatar][url]" id="book-author-avatar-url" value="#{val}">)
147
+ end
148
+
149
+ it "allows to override 'value' attribute" do
150
+ html = form_for("/books", values: values) do |f|
151
+ f.text_field "book.author.avatar.url", value: "https://hanami.test/another-avatar.jpg"
152
+ end
153
+
154
+ expect(html).to include %(<input type="text" name="book[author][avatar][url]" id="book-author-avatar-url" value="https://hanami.test/another-avatar.jpg">)
155
+ end
156
+ end
157
+
158
+ context "with filled params" do
159
+ let(:params) { {book: {author: {avatar: {url: val}}}} }
160
+ let(:val) { "https://hanami.test/avatar.png" }
161
+
162
+ it "renders with value" do
163
+ html = form_for("/books") do |f|
164
+ f.text_field "book.author.avatar.url"
165
+ end
166
+
167
+ expect(html).to include %(<input type="text" name="book[author][avatar][url]" id="book-author-avatar-url" value="#{val}">)
168
+ end
169
+
170
+ it "allows to override 'value' attribute" do
171
+ html = form_for("/books") do |f|
172
+ f.text_field "book.author.avatar.url", value: "https://hanami.test/another-avatar.jpg"
173
+ end
174
+
175
+ expect(html).to include %(<input type="text" name="book[author][avatar][url]" id="book-author-avatar-url" value="https://hanami.test/another-avatar.jpg">)
176
+ end
177
+ end
178
+ end
179
+
180
+ context "CSRF protection" do
181
+ let(:csrf_token) { "abc123" }
182
+
183
+ before do
184
+ allow(request).to receive(:session) { {_csrf_token: csrf_token} }
185
+ end
186
+
187
+ it "injects hidden field session is enabled" do
188
+ html = form_for("/books")
189
+ expect(html).to eq %(<form action="/books" accept-charset="utf-8" method="POST"><input type="hidden" name="_csrf_token" value="#{csrf_token}"></form>)
190
+ end
191
+
192
+ context "with missing token" do
193
+ let(:csrf_token) { nil }
194
+
195
+ it "doesn't inject hidden field" do
196
+ html = form_for("/books")
197
+ expect(html).to eq %(<form action="/books" accept-charset="utf-8" method="POST"></form>)
198
+ end
199
+ end
200
+
201
+ context "with csrf_token on get verb" do
202
+ it "doesn't inject hidden field" do
203
+ html = form_for("/books", method: "GET")
204
+ expect(html).to eq %(<form action="/books" method="GET" accept-charset="utf-8"></form>)
205
+ end
206
+ end
207
+
208
+ %i[patch put delete].each do |verb|
209
+ it "it injects hidden field when method override (#{verb}) is active" do
210
+ html = form_for("/books", method: verb)
211
+ expect(html).to eq %(<form action="/books" method="POST" accept-charset="utf-8"><input type="hidden" name="_method" value="#{verb.to_s.upcase}"><input type="hidden" name="_csrf_token" value="#{csrf_token}"></form>)
212
+ end
213
+ end
214
+ end
215
+
216
+ context "CSRF meta tags" do
217
+ let(:csrf_token) { "abc123" }
218
+
219
+ before do
220
+ allow(request).to receive(:session) { {_csrf_token: csrf_token} }
221
+ end
222
+
223
+ def csrf_meta_tags(...)
224
+ h { csrf_meta_tags(...) }
225
+ end
226
+
227
+ it "prints meta tags" do
228
+ html = csrf_meta_tags
229
+ expect(html).to eq %(<meta name="csrf-param" content="_csrf_token"><meta name="csrf-token" content="#{csrf_token}">)
230
+ end
231
+
232
+ context "when CSRF token is nil" do
233
+ let(:csrf_token) { nil }
234
+
235
+ it "returns nil" do
236
+ expect(csrf_meta_tags).to be(nil)
237
+ end
238
+ end
239
+ end
240
+
241
+ context "remote: true" do
242
+ it "adds data-remote=true to form attributes" do
243
+ html = form_for("/books", "data-remote": true)
244
+ expect(html).to eq %(<form action="/books" data-remote="true" accept-charset="utf-8" method="POST"></form>)
245
+ end
246
+
247
+ it "adds data-remote=false to form attributes" do
248
+ html = form_for("/books", "data-remote": false)
249
+ expect(html).to eq %(<form action="/books" data-remote="false" accept-charset="utf-8" method="POST"></form>)
250
+ end
251
+
252
+ it "adds data-remote= to form attributes" do
253
+ html = form_for("/books", "data-remote": nil)
254
+ expect(html).to eq %(<form action="/books" accept-charset="utf-8" method="POST"></form>)
255
+ end
256
+ end
257
+
258
+ context "explicitly given params" do
259
+ let(:params) { {song: {title: "Orphans"}} }
260
+ let(:given_params) { {song: {title: "Arabesque"}} }
261
+ let(:action) { "/songs" }
262
+
263
+ it "renders" do
264
+ html = form_for("/songs", params: given_params) do |f|
265
+ f.text_field "song.title"
266
+ end
267
+
268
+ expect(html).to eq(%(<form action="/songs" accept-charset="utf-8" method="POST"><input type="text" name="song[title]" id="song-title" value="Arabesque"></form>))
269
+ end
270
+ end
271
+ end
272
+
273
+ describe "#fields_for" do
274
+ it "renders" do
275
+ html = render(<<~ERB)
276
+ <%= form_for("/books") do |f| %>
277
+ <% f.fields_for "book.categories" do |fa| %>
278
+ <%= fa.text_field :name %>
279
+
280
+ <% fa.fields_for :subcategories do |fb| %>
281
+ <%= fb.text_field :name %>
282
+ <% end %>
283
+
284
+ <%= fa.text_field :name2 %>
285
+ <% end %>
286
+
287
+ <%= f.text_field "book.title" %>
288
+ <% end %>
289
+ ERB
290
+
291
+ expect(html).to eq_html <<~HTML
292
+ <form action="/books" accept-charset="utf-8" method="POST">
293
+ <input type="text" name="book[categories][name]" id="book-categories-name" value="">
294
+ <input type="text" name="book[categories][subcategories][name]" id="book-categories-subcategories-name" value="">
295
+ <input type="text" name="book[categories][name2]" id="book-categories-name2" value="">
296
+ <input type="text" name="book[title]" id="book-title" value="">
297
+ </form>
298
+ HTML
299
+ end
300
+
301
+ describe "with filled params" do
302
+ let(:params) { {book: {title: "TDD", categories: {name: "foo", name2: "bar", subcategories: {name: "sub"}}}} }
303
+
304
+ it "renders" do
305
+ html = render(<<~ERB)
306
+ <%= form_for("/books") do |f| %>
307
+ <% f.fields_for "book.categories" do |fa| %>
308
+ <%= fa.text_field :name %>
309
+
310
+ <% fa.fields_for :subcategories do |fb| %>
311
+ <%= fb.text_field :name %>
312
+ <% end %>
313
+
314
+ <%= fa.text_field :name2 %>
315
+ <% end %>
316
+
317
+ <%= f.text_field "book.title" %>
318
+ <% end %>
319
+ ERB
320
+
321
+ expect(html).to eq_html <<~HTML
322
+ <form action="/books" accept-charset="utf-8" method="POST">
323
+ <input type="text" name="book[categories][name]" id="book-categories-name" value="foo">
324
+ <input type="text" name="book[categories][subcategories][name]" id="book-categories-subcategories-name" value="sub">
325
+ <input type="text" name="book[categories][name2]" id="book-categories-name2" value="bar">
326
+ <input type="text" name="book[title]" id="book-title" value="TDD">
327
+ </form>
328
+ HTML
329
+ end
330
+ end
331
+ end
332
+
333
+ describe "#fields_for_collection" do
334
+ let(:params) { {book: {categories: [{name: "foo", new: true, genre: nil}]}} }
335
+
336
+ it "renders" do
337
+ html = render(<<~ERB)
338
+ <%= form_for("/books") do |f| %>
339
+ <% f.fields_for_collection "book.categories" do |fa| %>
340
+ <%= fa.text_field :name %>
341
+ <%= fa.hidden_field :name %>
342
+ <%= fa.text_area :name %>
343
+ <%= fa.check_box :new %>
344
+ <%= fa.select :genre, [%w[Terror terror], %w[Comedy comedy]] %>
345
+ <%= fa.color_field :name %>
346
+ <%= fa.date_field :name %>
347
+ <%= fa.datetime_field :name %>
348
+ <%= fa.datetime_local_field :name %>
349
+ <%= fa.time_field :name %>
350
+ <%= fa.month_field :name %>
351
+ <%= fa.week_field :name %>
352
+ <%= fa.email_field :name %>
353
+ <%= fa.url_field :name %>
354
+ <%= fa.tel_field :name %>
355
+ <%= fa.file_field :name %>
356
+ <%= fa.number_field :name %>
357
+ <%= fa.range_field :name %>
358
+ <%= fa.search_field :name %>
359
+ <%= fa.radio_button :name, "Fiction" %>
360
+ <%= fa.password_field :name %>
361
+ <%= fa.datalist :name, ["Italy", "United States"], "books" %>
362
+ <% end %>
363
+ <% end %>
364
+ ERB
365
+
366
+ expected = <<~HTML
367
+ <form action="/books" enctype="multipart/form-data" accept-charset="utf-8" method="POST">
368
+ <input type="text" name="book[categories][][name]" id="book-categories-0-name" value="foo">
369
+ <input type="hidden" name="book[categories][][name]" id="book-categories-0-name" value="foo">
370
+ <textarea name="book[categories][][name]" id="book-categories-0-name">
371
+ foo</textarea>
372
+ <input type="hidden" name="book[categories][][new]" value="0"><input type="checkbox" name="book[categories][][new]" id="book-categories-0-new" value="1" checked="checked">
373
+ <select name="book[categories][][genre]" id="book-categories-0-genre"><option value="terror">Terror</option><option value="comedy">Comedy</option></select>
374
+ <input type="color" name="book[categories][][name]" id="book-categories-0-name" value="foo">
375
+ <input type="date" name="book[categories][][name]" id="book-categories-0-name" value="foo">
376
+ <input type="datetime" name="book[categories][][name]" id="book-categories-0-name" value="foo">
377
+ <input type="datetime-local" name="book[categories][][name]" id="book-categories-0-name" value="foo">
378
+ <input type="time" name="book[categories][][name]" id="book-categories-0-name" value="foo">
379
+ <input type="month" name="book[categories][][name]" id="book-categories-0-name" value="foo">
380
+ <input type="week" name="book[categories][][name]" id="book-categories-0-name" value="foo">
381
+ <input type="email" name="book[categories][][name]" id="book-categories-0-name" value="foo">
382
+ <input type="url" name="book[categories][][name]" id="book-categories-0-name" value="">
383
+ <input type="tel" name="book[categories][][name]" id="book-categories-0-name" value="foo">
384
+ <input type="file" name="book[categories][][name]" id="book-categories-0-name">
385
+ <input type="number" name="book[categories][][name]" id="book-categories-0-name" value="foo">
386
+ <input type="range" name="book[categories][][name]" id="book-categories-0-name" value="foo">
387
+ <input type="search" name="book[categories][][name]" id="book-categories-0-name" value="foo">
388
+ <input type="radio" name="book[categories][][name]" value="Fiction">
389
+ <input type="password" name="book[categories][][name]" id="book-categories-0-name" value="">
390
+ <input type="text" name="book[categories][][name]" id="book-categories-0-name" value="foo" list="books"><datalist id="books"><option value="Italy"></option><option value="United States"></option></datalist>
391
+ </form>
392
+ HTML
393
+
394
+ expect(html).to eq_html(expected)
395
+ end
396
+ end
397
+
398
+ describe "#label" do
399
+ it "renders capitalized string" do
400
+ html = form_for("/books") do |f|
401
+ f.label "book.free_shipping"
402
+ end
403
+
404
+ expect(html).to include %(<label for="book-free-shipping">Free shipping</label>)
405
+ end
406
+
407
+ it "accepts a string as custom content" do
408
+ html = form_for("/books") do |f|
409
+ f.label "Free Shipping!", for: "book.free_shipping"
410
+ end
411
+
412
+ expect(html).to include %(<label for="book-free-shipping">Free Shipping!</label>)
413
+ end
414
+
415
+ it "renders a label with block" do
416
+ html = render(<<~ERB)
417
+ <%= form_for "/books" do |f| %>
418
+ <%= f.label for: "book.free_shipping" do %>
419
+ Free Shipping
420
+ <%= tag.abbr "*", title: "optional", aria: {label: "optional"} %>
421
+ <% end %>
422
+ <% end %>
423
+ ERB
424
+
425
+ expect(html).to eq <<~HTML
426
+ <form action="/books" accept-charset="utf-8" method="POST">
427
+ <label for="book-free-shipping">
428
+ Free Shipping
429
+ <abbr title="optional" aria-label="optional">*</abbr>
430
+ </label>
431
+ </form>
432
+ HTML
433
+ end
434
+ end
435
+
436
+ describe "#button" do
437
+ it "renders a button" do
438
+ html = form_for("/books") do |f|
439
+ f.button "Click me"
440
+ end
441
+
442
+ expect(html).to include %(<button>Click me</button>)
443
+ end
444
+
445
+ it "renders a button with HTML attributes" do
446
+ html = form_for("/books") do |f|
447
+ f.button "Click me", class: "btn btn-secondary"
448
+ end
449
+
450
+ expect(html).to include(%(<button class="btn btn-secondary">Click me</button>))
451
+ end
452
+
453
+ it "renders a button with block" do
454
+ html = render(<<~ERB)
455
+ <%= form_for("/books") do |f| %>
456
+ <%= f.button class: "btn btn-secondary" do %>
457
+ <%= tag.span class: "oi oi-check" %>
458
+ <% end %>
459
+ <% end %>
460
+ ERB
461
+
462
+ expect(html).to eq <<~HTML
463
+ <form action="/books" accept-charset="utf-8" method="POST">
464
+ <button class="btn btn-secondary">
465
+ <span class="oi oi-check"></span>
466
+ </button>
467
+ </form>
468
+ HTML
469
+ end
470
+ end
471
+
472
+ describe "#submit" do
473
+ it "renders a submit button" do
474
+ html = form_for("/books") do |f|
475
+ f.submit "Create"
476
+ end
477
+
478
+ expect(html).to include %(<button type="submit">Create</button>)
479
+ end
480
+
481
+ it "renders a submit button with HTML attributes" do
482
+ html = form_for("/books") do |f|
483
+ f.submit "Create", class: "btn btn-primary"
484
+ end
485
+
486
+ expect(html).to include %(<button type="submit" class="btn btn-primary">Create</button>)
487
+ end
488
+
489
+ it "renders a submit button with block" do
490
+ html = render(<<~ERB)
491
+ <%= form_for "/books" do |f| %>
492
+ <%= f.submit class: "btn btn-primary" do %>
493
+ <%= tag.span class: "oi oi-check" %>
494
+ <% end %>
495
+ <% end %>
496
+ ERB
497
+
498
+ expect(html).to eq <<~HTML
499
+ <form action="/books" accept-charset="utf-8" method="POST">
500
+ <button type="submit" class="btn btn-primary">
501
+ <span class="oi oi-check"></span>
502
+ </button>
503
+ </form>
504
+ HTML
505
+ end
506
+ end
507
+
508
+ describe "#image_button" do
509
+ it "renders an image button" do
510
+ html = form_for("/books") do |f|
511
+ f.image_button "https://hanamirb.org/assets/image_button.png"
512
+ end
513
+
514
+ expect(html).to include %(<input type="image" src="https://hanamirb.org/assets/image_button.png">)
515
+ end
516
+
517
+ it "renders an image button with HTML attributes" do
518
+ html = form_for("/books") do |f|
519
+ f.image_button "https://hanamirb.org/assets/image_button.png", name: "image", width: "50"
520
+ end
521
+
522
+ expect(html).to include %(<input name="image" width="50" type="image" src="https://hanamirb.org/assets/image_button.png">)
523
+ end
524
+
525
+ it "prevents XSS attacks" do
526
+ html = form_for("/books") do |f|
527
+ f.image_button "<script>alert('xss');</script>"
528
+ end
529
+
530
+ expect(html).to include %(<input type="image" src="">)
531
+ end
532
+ end
533
+
534
+ #
535
+ # FIELDSET
536
+ #
537
+
538
+ describe "#fieldset" do
539
+ it "renders a fieldset" do
540
+ html = render(<<~ERB)
541
+ <%= form_for "/books" do |f| %>
542
+ <%= f.fieldset do %>
543
+ <%= tag.legend "Author" %>
544
+ <%= f.label "author.name" %>
545
+ <%= f.text_field "author.name" %>
546
+ <% end %>
547
+ <% end %>
548
+ ERB
549
+
550
+ expect(html).to include_html <<~HTML
551
+ <fieldset>
552
+ <legend>Author</legend>
553
+ <label for="author-name">Name</label>
554
+ <input type="text" name="author[name]" id="author-name" value="">
555
+ </fieldset>
556
+ HTML
557
+ end
558
+ end
559
+
560
+ #
561
+ # INPUT FIELDS
562
+ #
563
+
564
+ describe "#check_box" do
565
+ it "renders" do
566
+ html = form_for("/books") do |f|
567
+ f.check_box "book.free_shipping"
568
+ end
569
+
570
+ expect(html).to include %(<input type="hidden" name="book[free_shipping]" value="0"><input type="checkbox" name="book[free_shipping]" id="book-free-shipping" value="1">)
571
+ end
572
+
573
+ it "allows to pass checked and unchecked value" do
574
+ html = form_for("/books") do |f|
575
+ f.check_box "book.free_shipping", checked_value: "true", unchecked_value: "false"
576
+ end
577
+
578
+ expect(html).to include %(<input type="hidden" name="book[free_shipping]" value="false"><input type="checkbox" name="book[free_shipping]" id="book-free-shipping" value="true">)
579
+ end
580
+
581
+ it "allows to override 'id' attribute" do
582
+ html = form_for("/books") do |f|
583
+ f.check_box "book.free_shipping", id: "shipping"
584
+ end
585
+
586
+ expect(html).to include %(<input type="hidden" name="book[free_shipping]" value="0"><input type="checkbox" name="book[free_shipping]" id="shipping" value="1">)
587
+ end
588
+
589
+ it "allows to override 'name' attribute" do
590
+ html = form_for("/books") do |f|
591
+ f.check_box "book.free_shipping", name: "book[free]"
592
+ end
593
+
594
+ expect(html).to include %(<input type="hidden" name="book[free]" value="0"><input type="checkbox" name="book[free]" id="book-free-shipping" value="1">)
595
+ end
596
+
597
+ it "allows to specify HTML attributes" do
598
+ html = form_for("/books") do |f|
599
+ f.check_box "book.free_shipping", class: "form-control"
600
+ end
601
+
602
+ expect(html).to include %(<input type="hidden" name="book[free_shipping]" value="0"><input type="checkbox" name="book[free_shipping]" id="book-free-shipping" value="1" class="form-control">)
603
+ end
604
+
605
+ it "doesn't render hidden field if 'value' attribute is specified" do
606
+ html = form_for("/books") do |f|
607
+ f.check_box "book.free_shipping", value: "ok"
608
+ end
609
+
610
+ expect(html).not_to include %(<input type="hidden" name="book[free_shipping]" value="0">)
611
+ expect(html).to include %(<input type="checkbox" name="book[free_shipping]" id="book-free-shipping" value="ok">)
612
+ end
613
+
614
+ it "renders hidden field if 'value' attribute and 'unchecked_value' option are both specified" do
615
+ html = form_for("/books") do |f|
616
+ f.check_box "book.free_shipping", value: "yes", unchecked_value: "no"
617
+ end
618
+
619
+ expect(html).to include %(<input type="hidden" name="book[free_shipping]" value="no"><input type="checkbox" name="book[free_shipping]" id="book-free-shipping" value="yes">)
620
+ end
621
+
622
+ it "handles multiple checkboxes" do
623
+ html = render(<<~ERB)
624
+ <%= form_for("/books") do |f| %>
625
+ <%= f.check_box "book.languages", name: "book[languages][]", value: "italian", id: nil %>
626
+ <%= f.check_box "book.languages", name: "book[languages][]", value: "english", id: nil %>
627
+ <% end %>
628
+ ERB
629
+
630
+ expect(html).to include_html <<~HTML
631
+ <input type="checkbox" name="book[languages][]" value="italian">
632
+ <input type="checkbox" name="book[languages][]" value="english">
633
+ HTML
634
+ end
635
+
636
+ context "with filled params" do
637
+ let(:params) { {book: {free_shipping: val}} }
638
+
639
+ context "when the params value equals to check box value" do
640
+ let(:val) { "1" }
641
+
642
+ it "renders with 'checked' attribute" do
643
+ html = form_for("/books") do |f|
644
+ f.check_box "book.free_shipping"
645
+ end
646
+
647
+ expect(html).to include %(<input type="hidden" name="book[free_shipping]" value="0"><input type="checkbox" name="book[free_shipping]" id="book-free-shipping" value="1" checked="checked">)
648
+ end
649
+ end
650
+
651
+ context "when the params value equals to the hidden field value" do
652
+ let(:val) { "0" }
653
+
654
+ it "renders without 'checked' attribute" do
655
+ html = form_for("/books") do |f|
656
+ f.check_box "book.free_shipping"
657
+ end
658
+
659
+ expect(html).to include %(<input type="hidden" name="book[free_shipping]" value="0"><input type="checkbox" name="book[free_shipping]" id="book-free-shipping" value="1">)
660
+ end
661
+
662
+ it "allows to override 'checked' attribute" do
663
+ html = form_for("/books") do |f|
664
+ f.check_box "book.free_shipping", checked: "checked"
665
+ end
666
+
667
+ expect(html).to include %(<input type="hidden" name="book[free_shipping]" value="0"><input type="checkbox" name="book[free_shipping]" id="book-free-shipping" value="1" checked="checked">)
668
+ end
669
+ end
670
+
671
+ context "with a boolean argument" do
672
+ let(:val) { true }
673
+
674
+ it "renders with 'checked' attribute" do
675
+ html = form_for("/books") do |f|
676
+ f.check_box "book.free_shipping"
677
+ end
678
+
679
+ expect(html).to include %(<input type="hidden" name="book[free_shipping]" value="0"><input type="checkbox" name="book[free_shipping]" id="book-free-shipping" value="1" checked="checked">)
680
+ end
681
+ end
682
+
683
+ context "when multiple params are present" do
684
+ let(:params) { {book: {languages: ["italian"]}} }
685
+
686
+ it "handles multiple checkboxes" do
687
+ html = render(<<~ERB)
688
+ <%= form_for("/books") do |f| %>
689
+ <%= f.check_box "book.languages", name: "book[languages][]", value: "italian", id: nil %>
690
+ <%= f.check_box "book.languages", name: "book[languages][]", value: "english", id: nil %>
691
+ <% end %>
692
+ ERB
693
+
694
+ expect(html).to include_html <<~HTML
695
+ <input type="checkbox" name="book[languages][]" value="italian" checked="checked">
696
+ <input type="checkbox" name="book[languages][]" value="english">
697
+ HTML
698
+ end
699
+ end
700
+
701
+ context "checked_value is boolean" do
702
+ let(:params) { {book: {free_shipping: "true"}} }
703
+
704
+ it "renders with 'checked' attribute" do
705
+ html = form_for("/books") do |f|
706
+ f.check_box "book.free_shipping", checked_value: true
707
+ end
708
+
709
+ expect(html).to include %(<input type="checkbox" name="book[free_shipping]" id="book-free-shipping" value="true" checked="checked">)
710
+ end
711
+ end
712
+ end
713
+
714
+ context "automatic values" do
715
+ context "checkbox" do
716
+ context "value boolean, helper boolean, values differ" do
717
+ let(:values) { {book: Struct.new(:free_shipping).new(false)} }
718
+
719
+ it "renders" do
720
+ html = form_for("/books", values: values) do |f|
721
+ f.check_box "book.free_shipping", checked_value: true
722
+ end
723
+
724
+ expect(html).to include %(<input type="checkbox" name="book[free_shipping]" id="book-free-shipping" value="true">)
725
+ end
726
+ end
727
+ end
728
+ end
729
+ end
730
+
731
+ describe "#color_field" do
732
+ it "renders" do
733
+ html = form_for("/books") do |f|
734
+ f.color_field "book.cover"
735
+ end
736
+
737
+ expect(html).to include %(<input type="color" name="book[cover]" id="book-cover" value="">)
738
+ end
739
+
740
+ it "allows to override 'id' attribute" do
741
+ html = form_for("/books") do |f|
742
+ f.color_field "book.cover", id: "b-cover"
743
+ end
744
+
745
+ expect(html).to include %(<input type="color" name="book[cover]" id="b-cover" value="">)
746
+ end
747
+
748
+ it "allows to override 'name' attribute" do
749
+ html = form_for("/books") do |f|
750
+ f.color_field "book.cover", name: "cover"
751
+ end
752
+
753
+ expect(html).to include %(<input type="color" name="cover" id="book-cover" value="">)
754
+ end
755
+
756
+ it "allows to override 'value' attribute" do
757
+ html = form_for("/books") do |f|
758
+ f.color_field "book.cover", value: "#ffffff"
759
+ end
760
+
761
+ expect(html).to include %(<input type="color" name="book[cover]" id="book-cover" value="#ffffff">)
762
+ end
763
+
764
+ it "allows to specify HTML attributes" do
765
+ html = form_for("/books") do |f|
766
+ f.color_field "book.cover", class: "form-control"
767
+ end
768
+
769
+ expect(html).to include %(<input type="color" name="book[cover]" id="book-cover" value="" class="form-control">)
770
+ end
771
+
772
+ context "with values" do
773
+ let(:values) { {book: Struct.new(:cover).new(val)} }
774
+ let(:val) { "#d3397e" }
775
+
776
+ it "renders with value" do
777
+ html = form_for("/books", values: values) do |f|
778
+ f.color_field "book.cover"
779
+ end
780
+
781
+ expect(html).to include %(<input type="color" name="book[cover]" id="book-cover" value="#{val}">)
782
+ end
783
+
784
+ it "allows to override 'value' attribute" do
785
+ html = form_for("/books", values: values) do |f|
786
+ f.color_field "book.cover", value: "#000000"
787
+ end
788
+
789
+ expect(html).to include %(<input type="color" name="book[cover]" id="book-cover" value="#000000">)
790
+ end
791
+ end
792
+
793
+ context "with filled params" do
794
+ let(:params) { {book: {cover: val}} }
795
+ let(:val) { "#d3397e" }
796
+
797
+ it "renders with value" do
798
+ html = form_for("/books") do |f|
799
+ f.color_field "book.cover"
800
+ end
801
+
802
+ expect(html).to include %(<input type="color" name="book[cover]" id="book-cover" value="#{val}">)
803
+ end
804
+
805
+ it "allows to override 'value' attribute" do
806
+ html = form_for("/books") do |f|
807
+ f.color_field "book.cover", value: "#000000"
808
+ end
809
+
810
+ expect(html).to include %(<input type="color" name="book[cover]" id="book-cover" value="#000000">)
811
+ end
812
+ end
813
+ end
814
+
815
+ describe "#date_field" do
816
+ it "renders" do
817
+ html = form_for("/books") do |f|
818
+ f.date_field "book.release_date"
819
+ end
820
+
821
+ expect(html).to include %(<input type="date" name="book[release_date]" id="book-release-date" value="">)
822
+ end
823
+
824
+ it "allows to override 'id' attribute" do
825
+ html = form_for("/books") do |f|
826
+ f.date_field "book.release_date", id: "release-date"
827
+ end
828
+
829
+ expect(html).to include %(<input type="date" name="book[release_date]" id="release-date" value="">)
830
+ end
831
+
832
+ it "allows to override 'name' attribute" do
833
+ html = form_for("/books") do |f|
834
+ f.date_field "book.release_date", name: "release_date"
835
+ end
836
+
837
+ expect(html).to include %(<input type="date" name="release_date" id="book-release-date" value="">)
838
+ end
839
+
840
+ it "allows to override 'value' attribute" do
841
+ html = form_for("/books") do |f|
842
+ f.date_field "book.release_date", value: "2015-02-19"
843
+ end
844
+
845
+ expect(html).to include %(<input type="date" name="book[release_date]" id="book-release-date" value="2015-02-19">)
846
+ end
847
+
848
+ it "allows to specify HTML attributes" do
849
+ html = form_for("/books") do |f|
850
+ f.date_field "book.release_date", class: "form-control"
851
+ end
852
+
853
+ expect(html).to include %(<input type="date" name="book[release_date]" id="book-release-date" value="" class="form-control">)
854
+ end
855
+
856
+ context "with values" do
857
+ let(:values) { {book: Struct.new(:release_date).new(val)} }
858
+ let(:val) { "2014-06-23" }
859
+
860
+ it "renders with value" do
861
+ html = form_for("/books", values: values) do |f|
862
+ f.date_field "book.release_date"
863
+ end
864
+
865
+ expect(html).to include %(<input type="date" name="book[release_date]" id="book-release-date" value="#{val}">)
866
+ end
867
+
868
+ it "allows to override 'value' attribute" do
869
+ html = form_for("/books", values: values) do |f|
870
+ f.date_field "book.release_date", value: "2015-03-23"
871
+ end
872
+
873
+ expect(html).to include %(<input type="date" name="book[release_date]" id="book-release-date" value="2015-03-23">)
874
+ end
875
+ end
876
+
877
+ context "with filled params" do
878
+ let(:params) { {book: {release_date: val}} }
879
+ let(:val) { "2014-06-23" }
880
+
881
+ it "renders with value" do
882
+ html = form_for("/books") do |f|
883
+ f.date_field "book.release_date"
884
+ end
885
+
886
+ expect(html).to include %(<input type="date" name="book[release_date]" id="book-release-date" value="#{val}">)
887
+ end
888
+
889
+ it "allows to override 'value' attribute" do
890
+ html = form_for("/books") do |f|
891
+ f.date_field "book.release_date", value: "2015-03-23"
892
+ end
893
+
894
+ expect(html).to include %(<input type="date" name="book[release_date]" id="book-release-date" value="2015-03-23">)
895
+ end
896
+ end
897
+ end
898
+
899
+ describe "#datetime_field" do
900
+ it "renders" do
901
+ html = form_for("/books") do |f|
902
+ f.datetime_field "book.published_at"
903
+ end
904
+
905
+ expect(html).to include %(<input type="datetime" name="book[published_at]" id="book-published-at" value="">)
906
+ end
907
+
908
+ it "allows to override 'id' attribute" do
909
+ html = form_for("/books") do |f|
910
+ f.datetime_field "book.published_at", id: "published-timestamp"
911
+ end
912
+
913
+ expect(html).to include %(<input type="datetime" name="book[published_at]" id="published-timestamp" value="">)
914
+ end
915
+
916
+ it "allows to override 'name' attribute" do
917
+ html = form_for("/books") do |f|
918
+ f.datetime_field "book.published_at", name: "book[published][timestamp]"
919
+ end
920
+
921
+ expect(html).to include %(<input type="datetime" name="book[published][timestamp]" id="book-published-at" value="">)
922
+ end
923
+
924
+ it "allows to override 'value' attribute" do
925
+ html = form_for("/books") do |f|
926
+ f.datetime_field "book.published_at", value: "2015-02-19T12:50:36Z"
927
+ end
928
+
929
+ expect(html).to include %(<input type="datetime" name="book[published_at]" id="book-published-at" value="2015-02-19T12:50:36Z">)
930
+ end
931
+
932
+ it "allows to specify HTML attributes" do
933
+ html = form_for("/books") do |f|
934
+ f.datetime_field "book.published_at", class: "form-control"
935
+ end
936
+
937
+ expect(html).to include %(<input type="datetime" name="book[published_at]" id="book-published-at" value="" class="form-control">)
938
+ end
939
+
940
+ context "with values" do
941
+ let(:values) { {book: Struct.new(:published_at).new(val)} }
942
+ let(:val) { "2015-02-19T12:56:31Z" }
943
+
944
+ it "renders with value" do
945
+ html = form_for("/books", values: values) do |f|
946
+ f.datetime_field "book.published_at"
947
+ end
948
+
949
+ expect(html).to include %(<input type="datetime" name="book[published_at]" id="book-published-at" value="#{val}">)
950
+ end
951
+
952
+ it "allows to override 'value' attribute" do
953
+ html = form_for("/books", values: values) do |f|
954
+ f.datetime_field "book.published_at", value: "2015-02-19T12:50:36Z"
955
+ end
956
+
957
+ expect(html).to include %(<input type="datetime" name="book[published_at]" id="book-published-at" value="2015-02-19T12:50:36Z">)
958
+ end
959
+ end
960
+
961
+ context "with filled params" do
962
+ let(:params) { {book: {published_at: val}} }
963
+ let(:val) { "2015-02-19T12:56:31Z" }
964
+
965
+ it "renders with value" do
966
+ html = form_for("/books") do |f|
967
+ f.datetime_field "book.published_at"
968
+ end
969
+
970
+ expect(html).to include %(<input type="datetime" name="book[published_at]" id="book-published-at" value="#{val}">)
971
+ end
972
+
973
+ it "allows to override 'value' attribute" do
974
+ html = form_for("/books") do |f|
975
+ f.datetime_field "book.published_at", value: "2015-02-19T12:50:36Z"
976
+ end
977
+
978
+ expect(html).to include %(<input type="datetime" name="book[published_at]" id="book-published-at" value="2015-02-19T12:50:36Z">)
979
+ end
980
+ end
981
+ end
982
+
983
+ describe "#datetime_local_field" do
984
+ it "renders" do
985
+ html = form_for("/books") do |f|
986
+ f.datetime_local_field "book.released_at"
987
+ end
988
+
989
+ expect(html).to include %(<input type="datetime-local" name="book[released_at]" id="book-released-at" value="">)
990
+ end
991
+
992
+ it "allows to override 'id' attribute" do
993
+ html = form_for("/books") do |f|
994
+ f.datetime_local_field "book.released_at", id: "local-release-timestamp"
995
+ end
996
+
997
+ expect(html).to include %(<input type="datetime-local" name="book[released_at]" id="local-release-timestamp" value="">)
998
+ end
999
+
1000
+ it "allows to override 'name' attribute" do
1001
+ html = form_for("/books") do |f|
1002
+ f.datetime_local_field "book.released_at", name: "book[release-timestamp]"
1003
+ end
1004
+
1005
+ expect(html).to include %(<input type="datetime-local" name="book[release-timestamp]" id="book-released-at" value="">)
1006
+ end
1007
+
1008
+ it "allows to override 'value' attribute" do
1009
+ html = form_for("/books") do |f|
1010
+ f.datetime_local_field "book.released_at", value: "2015-02-19T14:01:28+01:00"
1011
+ end
1012
+
1013
+ expect(html).to include %(<input type="datetime-local" name="book[released_at]" id="book-released-at" value="2015-02-19T14:01:28+01:00">)
1014
+ end
1015
+
1016
+ it "allows to specify HTML attributes" do
1017
+ html = form_for("/books") do |f|
1018
+ f.datetime_local_field "book.released_at", class: "form-control"
1019
+ end
1020
+
1021
+ expect(html).to include %(<input type="datetime-local" name="book[released_at]" id="book-released-at" value="" class="form-control">)
1022
+ end
1023
+
1024
+ context "with filled params" do
1025
+ let(:params) { {book: {released_at: val}} }
1026
+ let(:val) { "2015-02-19T14:11:19+01:00" }
1027
+
1028
+ it "renders with value" do
1029
+ html = form_for("/books") do |f|
1030
+ f.datetime_local_field "book.released_at"
1031
+ end
1032
+
1033
+ expect(html).to include %(<input type="datetime-local" name="book[released_at]" id="book-released-at" value="#{val}">)
1034
+ end
1035
+
1036
+ it "allows to override 'value' attribute" do
1037
+ html = form_for("/books") do |f|
1038
+ f.datetime_local_field "book.released_at", value: "2015-02-19T14:01:28+01:00"
1039
+ end
1040
+
1041
+ expect(html).to include %(<input type="datetime-local" name="book[released_at]" id="book-released-at" value="2015-02-19T14:01:28+01:00">)
1042
+ end
1043
+ end
1044
+ end
1045
+
1046
+ describe "#time_field" do
1047
+ it "renders" do
1048
+ html = form_for("/books") do |f|
1049
+ f.time_field "book.release_hour"
1050
+ end
1051
+
1052
+ expect(html).to include %(<input type="time" name="book[release_hour]" id="book-release-hour" value="">)
1053
+ end
1054
+
1055
+ it "allows to override 'id' attribute" do
1056
+ html = form_for("/books") do |f|
1057
+ f.time_field "book.release_hour", id: "release-hour"
1058
+ end
1059
+
1060
+ expect(html).to include %(<input type="time" name="book[release_hour]" id="release-hour" value="">)
1061
+ end
1062
+
1063
+ it "allows to override 'name' attribute" do
1064
+ html = form_for("/books") do |f|
1065
+ f.time_field "book.release_hour", name: "release_hour"
1066
+ end
1067
+
1068
+ expect(html).to include %(<input type="time" name="release_hour" id="book-release-hour" value="">)
1069
+ end
1070
+
1071
+ it "allows to override 'value' attribute" do
1072
+ html = form_for("/books") do |f|
1073
+ f.time_field "book.release_hour", value: "00:00"
1074
+ end
1075
+
1076
+ expect(html).to include %(<input type="time" name="book[release_hour]" id="book-release-hour" value="00:00">)
1077
+ end
1078
+
1079
+ it "allows to specify HTML attributes" do
1080
+ html = form_for("/books") do |f|
1081
+ f.time_field "book.release_hour", class: "form-control"
1082
+ end
1083
+
1084
+ expect(html).to include %(<input type="time" name="book[release_hour]" id="book-release-hour" value="" class="form-control">)
1085
+ end
1086
+
1087
+ context "with values" do
1088
+ let(:values) { {book: Struct.new(:release_hour).new(val)} }
1089
+ let(:val) { "18:30" }
1090
+
1091
+ it "renders with value" do
1092
+ html = form_for("/books", values: values) do |f|
1093
+ f.time_field "book.release_hour"
1094
+ end
1095
+
1096
+ expect(html).to include %(<input type="time" name="book[release_hour]" id="book-release-hour" value="#{val}">)
1097
+ end
1098
+
1099
+ it "allows to override 'value' attribute" do
1100
+ html = form_for("/books", values: values) do |f|
1101
+ f.time_field "book.release_hour", value: "17:00"
1102
+ end
1103
+
1104
+ expect(html).to include %(<input type="time" name="book[release_hour]" id="book-release-hour" value="17:00">)
1105
+ end
1106
+ end
1107
+
1108
+ context "with filled params" do
1109
+ let(:params) { {book: {release_hour: val}} }
1110
+ let(:val) { "11:30" }
1111
+
1112
+ it "renders with value" do
1113
+ html = form_for("/books") do |f|
1114
+ f.time_field "book.release_hour"
1115
+ end
1116
+
1117
+ expect(html).to include %(<input type="time" name="book[release_hour]" id="book-release-hour" value="#{val}">)
1118
+ end
1119
+
1120
+ it "allows to override 'value' attribute" do
1121
+ html = form_for("/books") do |f|
1122
+ f.time_field "book.release_hour", value: "8:15"
1123
+ end
1124
+
1125
+ expect(html).to include %(<input type="time" name="book[release_hour]" id="book-release-hour" value="8:15">)
1126
+ end
1127
+ end
1128
+ end
1129
+
1130
+ describe "#month_field" do
1131
+ it "renders" do
1132
+ html = form_for("/books") do |f|
1133
+ f.month_field "book.release_month"
1134
+ end
1135
+
1136
+ expect(html).to include %(<input type="month" name="book[release_month]" id="book-release-month" value="">)
1137
+ end
1138
+
1139
+ it "allows to override 'id' attribute" do
1140
+ html = form_for("/books") do |f|
1141
+ f.month_field "book.release_month", id: "release-month"
1142
+ end
1143
+
1144
+ expect(html).to include %(<input type="month" name="book[release_month]" id="release-month" value="">)
1145
+ end
1146
+
1147
+ it "allows to override 'name' attribute" do
1148
+ html = form_for("/books") do |f|
1149
+ f.month_field "book.release_month", name: "release_month"
1150
+ end
1151
+
1152
+ expect(html).to include %(<input type="month" name="release_month" id="book-release-month" value="">)
1153
+ end
1154
+
1155
+ it "allows to override 'value' attribute" do
1156
+ html = form_for("/books") do |f|
1157
+ f.month_field "book.release_month", value: "2017-03"
1158
+ end
1159
+
1160
+ expect(html).to include %(<input type="month" name="book[release_month]" id="book-release-month" value="2017-03">)
1161
+ end
1162
+
1163
+ it "allows to specify HTML attributes" do
1164
+ html = form_for("/books") do |f|
1165
+ f.month_field "book.release_month", class: "form-control"
1166
+ end
1167
+
1168
+ expect(html).to include %(<input type="month" name="book[release_month]" id="book-release-month" value="" class="form-control">)
1169
+ end
1170
+
1171
+ context "with values" do
1172
+ let(:values) { {book: Struct.new(:release_month).new(val)} }
1173
+ let(:val) { "2017-03" }
1174
+
1175
+ it "renders with value" do
1176
+ html = form_for("/books", values: values) do |f|
1177
+ f.month_field "book.release_month"
1178
+ end
1179
+
1180
+ expect(html).to include %(<input type="month" name="book[release_month]" id="book-release-month" value="#{val}">)
1181
+ end
1182
+
1183
+ it "allows to override 'value' attribute" do
1184
+ html = form_for("/books", values: values) do |f|
1185
+ f.month_field "book.release_month", value: "2017-04"
1186
+ end
1187
+
1188
+ expect(html).to include %(<input type="month" name="book[release_month]" id="book-release-month" value="2017-04">)
1189
+ end
1190
+ end
1191
+
1192
+ context "with filled params" do
1193
+ let(:params) { {book: {release_month: val}} }
1194
+ let(:val) { "2017-10" }
1195
+
1196
+ it "renders with value" do
1197
+ html = form_for("/books") do |f|
1198
+ f.month_field "book.release_month"
1199
+ end
1200
+
1201
+ expect(html).to include %(<input type="month" name="book[release_month]" id="book-release-month" value="#{val}">)
1202
+ end
1203
+
1204
+ it "allows to override 'value' attribute" do
1205
+ html = form_for("/books") do |f|
1206
+ f.month_field "book.release_month", value: "2017-04"
1207
+ end
1208
+
1209
+ expect(html).to include %(<input type="month" name="book[release_month]" id="book-release-month" value="2017-04">)
1210
+ end
1211
+ end
1212
+ end
1213
+
1214
+ describe "#week_field" do
1215
+ it "renders" do
1216
+ html = form_for("/books") do |f|
1217
+ f.week_field "book.release_week"
1218
+ end
1219
+
1220
+ expect(html).to include %(<input type="week" name="book[release_week]" id="book-release-week" value="">)
1221
+ end
1222
+
1223
+ it "allows to override 'id' attribute" do
1224
+ html = form_for("/books") do |f|
1225
+ f.week_field "book.release_week", id: "release-week"
1226
+ end
1227
+
1228
+ expect(html).to include %(<input type="week" name="book[release_week]" id="release-week" value="">)
1229
+ end
1230
+
1231
+ it "allows to override 'name' attribute" do
1232
+ html = form_for("/books") do |f|
1233
+ f.week_field "book.release_week", name: "release_week"
1234
+ end
1235
+
1236
+ expect(html).to include %(<input type="week" name="release_week" id="book-release-week" value="">)
1237
+ end
1238
+
1239
+ it "allows to override 'value' attribute" do
1240
+ html = form_for("/books") do |f|
1241
+ f.week_field "book.release_week", value: "2017-W10"
1242
+ end
1243
+
1244
+ expect(html).to include %(<input type="week" name="book[release_week]" id="book-release-week" value="2017-W10">)
1245
+ end
1246
+
1247
+ it "allows to specify HTML attributes" do
1248
+ html = form_for("/books") do |f|
1249
+ f.week_field "book.release_week", class: "form-control"
1250
+ end
1251
+
1252
+ expect(html).to include %(<input type="week" name="book[release_week]" id="book-release-week" value="" class="form-control">)
1253
+ end
1254
+
1255
+ context "with values" do
1256
+ let(:values) { {book: Struct.new(:release_week).new(val)} }
1257
+ let(:val) { "2017-W10" }
1258
+
1259
+ it "renders with value" do
1260
+ html = form_for("/books", values: values) do |f|
1261
+ f.week_field "book.release_week"
1262
+ end
1263
+
1264
+ expect(html).to include %(<input type="week" name="book[release_week]" id="book-release-week" value="#{val}">)
1265
+ end
1266
+
1267
+ it "allows to override 'value' attribute" do
1268
+ html = form_for("/books", values: values) do |f|
1269
+ f.week_field "book.release_week", value: "2017-W31"
1270
+ end
1271
+
1272
+ expect(html).to include %(<input type="week" name="book[release_week]" id="book-release-week" value="2017-W31">)
1273
+ end
1274
+ end
1275
+
1276
+ context "with filled params" do
1277
+ let(:params) { {book: {release_week: val}} }
1278
+ let(:val) { "2017-W44" }
1279
+
1280
+ it "renders with value" do
1281
+ html = form_for("/books") do |f|
1282
+ f.week_field "book.release_week"
1283
+ end
1284
+
1285
+ expect(html).to include %(<input type="week" name="book[release_week]" id="book-release-week" value="#{val}">)
1286
+ end
1287
+
1288
+ it "allows to override 'value' attribute" do
1289
+ html = form_for("/books") do |f|
1290
+ f.week_field "book.release_week", value: "2017-W07"
1291
+ end
1292
+
1293
+ expect(html).to include %(<input type="week" name="book[release_week]" id="book-release-week" value="2017-W07">)
1294
+ end
1295
+ end
1296
+ end
1297
+
1298
+ describe "#email_field" do
1299
+ it "renders" do
1300
+ html = form_for("/books") do |f|
1301
+ f.email_field "book.publisher_email"
1302
+ end
1303
+
1304
+ expect(html).to include %(<input type="email" name="book[publisher_email]" id="book-publisher-email" value="">)
1305
+ end
1306
+
1307
+ it "allows to override 'id' attribute" do
1308
+ html = form_for("/books") do |f|
1309
+ f.email_field "book.publisher_email", id: "publisher-email"
1310
+ end
1311
+
1312
+ expect(html).to include %(<input type="email" name="book[publisher_email]" id="publisher-email" value="">)
1313
+ end
1314
+
1315
+ it "allows to override 'name' attribute" do
1316
+ html = form_for("/books") do |f|
1317
+ f.email_field "book.publisher_email", name: "book[email]"
1318
+ end
1319
+
1320
+ expect(html).to include %(<input type="email" name="book[email]" id="book-publisher-email" value="">)
1321
+ end
1322
+
1323
+ it "allows to override 'value' attribute" do
1324
+ html = form_for("/books") do |f|
1325
+ f.email_field "book.publisher_email", value: "publisher@example.org"
1326
+ end
1327
+
1328
+ expect(html).to include %(<input type="email" name="book[publisher_email]" id="book-publisher-email" value="publisher@example.org">)
1329
+ end
1330
+
1331
+ it "allows to specify 'multiple' attribute" do
1332
+ html = form_for("/books") do |f|
1333
+ f.email_field "book.publisher_email", multiple: true
1334
+ end
1335
+
1336
+ expect(html).to include %(<input type="email" name="book[publisher_email]" id="book-publisher-email" value="" multiple="multiple">)
1337
+ end
1338
+
1339
+ it "allows to specify HTML attributes" do
1340
+ html = form_for("/books") do |f|
1341
+ f.email_field "book.publisher_email", class: "form-control"
1342
+ end
1343
+
1344
+ expect(html).to include %(<input type="email" name="book[publisher_email]" id="book-publisher-email" value="" class="form-control">)
1345
+ end
1346
+
1347
+ context "with values" do
1348
+ let(:values) { {book: Struct.new(:publisher_email).new(val)} }
1349
+ let(:val) { "maria@publisher.org" }
1350
+
1351
+ it "renders with value" do
1352
+ html = form_for("/books", values: values) do |f|
1353
+ f.email_field "book.publisher_email"
1354
+ end
1355
+
1356
+ expect(html).to include %(<input type="email" name="book[publisher_email]" id="book-publisher-email" value="maria@publisher.org">)
1357
+ end
1358
+
1359
+ it "allows to override 'value' attribute" do
1360
+ html = form_for("/books", values: values) do |f|
1361
+ f.email_field "book.publisher_email", value: "publisher@example.org"
1362
+ end
1363
+
1364
+ expect(html).to include %(<input type="email" name="book[publisher_email]" id="book-publisher-email" value="publisher@example.org">)
1365
+ end
1366
+ end
1367
+
1368
+ context "with filled params" do
1369
+ let(:params) { {book: {publisher_email: val}} }
1370
+ let(:val) { "maria@publisher.org" }
1371
+
1372
+ it "renders with value" do
1373
+ html = form_for("/books") do |f|
1374
+ f.email_field "book.publisher_email"
1375
+ end
1376
+
1377
+ expect(html).to include %(<input type="email" name="book[publisher_email]" id="book-publisher-email" value="maria@publisher.org">)
1378
+ end
1379
+
1380
+ it "allows to override 'value' attribute" do
1381
+ html = form_for("/books") do |f|
1382
+ f.email_field "book.publisher_email", value: "publisher@example.org"
1383
+ end
1384
+
1385
+ expect(html).to include %(<input type="email" name="book[publisher_email]" id="book-publisher-email" value="publisher@example.org">)
1386
+ end
1387
+ end
1388
+ end
1389
+
1390
+ describe "#url_field" do
1391
+ it "renders" do
1392
+ html = form_for("/books") do |f|
1393
+ f.url_field "book.website"
1394
+ end
1395
+
1396
+ expect(html).to include %(<input type="url" name="book[website]" id="book-website" value="">)
1397
+ end
1398
+
1399
+ it "allows to override 'id' attribute" do
1400
+ html = form_for("/books") do |f|
1401
+ f.url_field "book.website", id: "website"
1402
+ end
1403
+
1404
+ expect(html).to include %(<input type="url" name="book[website]" id="website" value="">)
1405
+ end
1406
+
1407
+ it "allows to override 'name' attribute" do
1408
+ html = form_for("/books") do |f|
1409
+ f.url_field "book.website", name: "book[url]"
1410
+ end
1411
+
1412
+ expect(html).to include %(<input type="url" name="book[url]" id="book-website" value="">)
1413
+ end
1414
+
1415
+ it "allows to override 'value' attribute" do
1416
+ html = form_for("/books") do |f|
1417
+ f.url_field "book.website", value: "http://example.org"
1418
+ end
1419
+
1420
+ expect(html).to include %(<input type="url" name="book[website]" id="book-website" value="http://example.org">)
1421
+ end
1422
+
1423
+ it "allows to specify 'multiple' attribute" do
1424
+ html = form_for("/books") do |f|
1425
+ f.url_field "book.website", multiple: true
1426
+ end
1427
+
1428
+ expect(html).to include %(<input type="url" name="book[website]" id="book-website" value="" multiple="multiple">)
1429
+ end
1430
+
1431
+ it "allows to specify HTML attributes" do
1432
+ html = form_for("/books") do |f|
1433
+ f.url_field "book.website", class: "form-control"
1434
+ end
1435
+
1436
+ expect(html).to include %(<input type="url" name="book[website]" id="book-website" value="" class="form-control">)
1437
+ end
1438
+
1439
+ context "with values" do
1440
+ let(:values) { {book: Struct.new(:website).new(val)} }
1441
+ let(:val) { "http://publisher.org" }
1442
+
1443
+ it "renders with value" do
1444
+ html = form_for("/books", values: values) do |f|
1445
+ f.url_field "book.website"
1446
+ end
1447
+
1448
+ expect(html).to include %(<input type="url" name="book[website]" id="book-website" value="http://publisher.org">)
1449
+ end
1450
+
1451
+ it "allows to override 'value' attribute" do
1452
+ html = form_for("/books", values: values) do |f|
1453
+ f.url_field "book.website", value: "https://www.example.org"
1454
+ end
1455
+
1456
+ expect(html).to include %(<input type="url" name="book[website]" id="book-website" value="https://www.example.org">)
1457
+ end
1458
+ end
1459
+
1460
+ context "with filled params" do
1461
+ let(:params) { {book: {website: val}} }
1462
+ let(:val) { "http://publisher.org" }
1463
+
1464
+ it "renders with value" do
1465
+ html = form_for("/books") do |f|
1466
+ f.url_field "book.website"
1467
+ end
1468
+
1469
+ expect(html).to include %(<input type="url" name="book[website]" id="book-website" value="http://publisher.org">)
1470
+ end
1471
+
1472
+ it "allows to override 'value' attribute" do
1473
+ html = form_for("/books") do |f|
1474
+ f.url_field "book.website", value: "http://example.org"
1475
+ end
1476
+
1477
+ expect(html).to include %(<input type="url" name="book[website]" id="book-website" value="http://example.org">)
1478
+ end
1479
+ end
1480
+
1481
+ context "with escape url" do
1482
+ let(:values) { {book: Struct.new(:website).new(val)} }
1483
+ let(:val) { %("onclick=javascript:alert('xss')) }
1484
+
1485
+ it "renders with automatic value" do
1486
+ html = form_for("/books", values: values) do |f|
1487
+ f.url_field "book.website"
1488
+ end
1489
+
1490
+ expect(html).to include %(<input type="url" name="book[website]" id="book-website" value="">)
1491
+ end
1492
+
1493
+ it "renders with explicit value" do
1494
+ html = form_for("/books", values: values) do |f|
1495
+ f.url_field "book.website", value: val
1496
+ end
1497
+
1498
+ expect(html).to include %(<input type="url" name="book[website]" id="book-website" value="">)
1499
+ end
1500
+ end
1501
+ end
1502
+
1503
+ describe "#tel_field" do
1504
+ it "renders" do
1505
+ html = form_for("/books") do |f|
1506
+ f.tel_field "book.publisher_telephone"
1507
+ end
1508
+
1509
+ expect(html).to include %(<input type="tel" name="book[publisher_telephone]" id="book-publisher-telephone" value="">)
1510
+ end
1511
+
1512
+ it "allows to override 'id' attribute" do
1513
+ html = form_for("/books") do |f|
1514
+ f.tel_field "book.publisher_telephone", id: "publisher-telephone"
1515
+ end
1516
+
1517
+ expect(html).to include %(<input type="tel" name="book[publisher_telephone]" id="publisher-telephone" value="">)
1518
+ end
1519
+
1520
+ it "allows to override 'name' attribute" do
1521
+ html = form_for("/books") do |f|
1522
+ f.tel_field "book.publisher_telephone", name: "book[telephone]"
1523
+ end
1524
+
1525
+ expect(html).to include %(<input type="tel" name="book[telephone]" id="book-publisher-telephone" value="">)
1526
+ end
1527
+
1528
+ it "allows to override 'value' attribute" do
1529
+ html = form_for("/books") do |f|
1530
+ f.tel_field "book.publisher_telephone", value: "publisher@example.org"
1531
+ end
1532
+
1533
+ expect(html).to include %(<input type="tel" name="book[publisher_telephone]" id="book-publisher-telephone" value="publisher@example.org">)
1534
+ end
1535
+
1536
+ it "allows to specify 'multiple' attribute" do
1537
+ html = form_for("/books") do |f|
1538
+ f.tel_field "book.publisher_telephone", multiple: true
1539
+ end
1540
+
1541
+ expect(html).to include %(<input type="tel" name="book[publisher_telephone]" id="book-publisher-telephone" value="" multiple="multiple">)
1542
+ end
1543
+
1544
+ it "allows to specify HTML attributes" do
1545
+ html = form_for("/books") do |f|
1546
+ f.tel_field "book.publisher_telephone", class: "form-control"
1547
+ end
1548
+
1549
+ expect(html).to include %(<input type="tel" name="book[publisher_telephone]" id="book-publisher-telephone" value="" class="form-control">)
1550
+ end
1551
+
1552
+ context "with values" do
1553
+ let(:values) { {book: Struct.new(:publisher_telephone).new(val)} }
1554
+ let(:val) { "maria@publisher.org" }
1555
+
1556
+ it "renders with value" do
1557
+ html = form_for("/books", values: values) do |f|
1558
+ f.tel_field "book.publisher_telephone"
1559
+ end
1560
+
1561
+ expect(html).to include %(<input type="tel" name="book[publisher_telephone]" id="book-publisher-telephone" value="maria@publisher.org">)
1562
+ end
1563
+
1564
+ it "allows to override 'value' attribute" do
1565
+ html = form_for("/books", values: values) do |f|
1566
+ f.tel_field "book.publisher_telephone", value: "publisher@example.org"
1567
+ end
1568
+
1569
+ expect(html).to include %(<input type="tel" name="book[publisher_telephone]" id="book-publisher-telephone" value="publisher@example.org">)
1570
+ end
1571
+ end
1572
+
1573
+ context "with filled params" do
1574
+ let(:params) { {book: {publisher_telephone: val}} }
1575
+ let(:val) { "maria@publisher.org" }
1576
+
1577
+ it "renders with value" do
1578
+ html = form_for("/books") do |f|
1579
+ f.tel_field "book.publisher_telephone"
1580
+ end
1581
+
1582
+ expect(html).to include %(<input type="tel" name="book[publisher_telephone]" id="book-publisher-telephone" value="maria@publisher.org">)
1583
+ end
1584
+
1585
+ it "allows to override 'value' attribute" do
1586
+ html = form_for("/books") do |f|
1587
+ f.tel_field "book.publisher_telephone", value: "publisher@example.org"
1588
+ end
1589
+
1590
+ expect(html).to include %(<input type="tel" name="book[publisher_telephone]" id="book-publisher-telephone" value="publisher@example.org">)
1591
+ end
1592
+ end
1593
+ end
1594
+
1595
+ describe "#file_field" do
1596
+ it "renders" do
1597
+ html = form_for("/books") do |f|
1598
+ f.file_field "book.image_cover"
1599
+ end
1600
+
1601
+ expect(html).to include %(<input type="file" name="book[image_cover]" id="book-image-cover">)
1602
+ end
1603
+
1604
+ it "sets 'enctype' attribute to the form" do
1605
+ html = form_for("/books") do |f|
1606
+ f.file_field "book.image_cover"
1607
+ end
1608
+
1609
+ expect(html).to include %(<form action="/books" enctype="multipart/form-data" accept-charset="utf-8" method="POST">)
1610
+ end
1611
+
1612
+ it "sets 'enctype' attribute to the form when there are nested fields" do
1613
+ html = form_for("/books") do |f|
1614
+ f.fields_for("images") do
1615
+ f.file_field("cover")
1616
+ end
1617
+ end
1618
+
1619
+ expect(html).to include %(<form action="/books" enctype="multipart/form-data" accept-charset="utf-8" method="POST">)
1620
+ end
1621
+
1622
+ it "allows to override 'id' attribute" do
1623
+ html = form_for("/books") do |f|
1624
+ f.file_field "book.image_cover", id: "book-cover"
1625
+ end
1626
+
1627
+ expect(html).to include %(<input type="file" name="book[image_cover]" id="book-cover">)
1628
+ end
1629
+
1630
+ it "allows to override 'name' attribute" do
1631
+ html = form_for("/books") do |f|
1632
+ f.file_field "book.image_cover", name: "book[cover]"
1633
+ end
1634
+
1635
+ expect(html).to include %(<input type="file" name="book[cover]" id="book-image-cover">)
1636
+ end
1637
+
1638
+ it "allows to specify 'multiple' attribute" do
1639
+ html = form_for("/books") do |f|
1640
+ f.file_field "book.image_cover", multiple: true
1641
+ end
1642
+
1643
+ expect(html).to include %(<input type="file" name="book[image_cover]" id="book-image-cover" multiple="multiple">)
1644
+ end
1645
+
1646
+ it "allows to specify single value for 'accept' attribute" do
1647
+ html = form_for("/books") do |f|
1648
+ f.file_field "book.image_cover", accept: "application/pdf"
1649
+ end
1650
+
1651
+ expect(html).to include %(<input type="file" name="book[image_cover]" id="book-image-cover" accept="application/pdf">)
1652
+ end
1653
+
1654
+ it "allows to specify multiple values for 'accept' attribute" do
1655
+ html = form_for("/books") do |f|
1656
+ f.file_field "book.image_cover", accept: "image/png,image/jpg"
1657
+ end
1658
+
1659
+ expect(html).to include %(<input type="file" name="book[image_cover]" id="book-image-cover" accept="image/png,image/jpg">)
1660
+ end
1661
+
1662
+ it "allows to specify multiple values (array) for 'accept' attribute" do
1663
+ html = form_for("/books") do |f|
1664
+ f.file_field "book.image_cover", accept: ["image/png", "image/jpg"]
1665
+ end
1666
+
1667
+ expect(html).to include %(<input type="file" name="book[image_cover]" id="book-image-cover" accept="image/png,image/jpg">)
1668
+ end
1669
+
1670
+ context "with values" do
1671
+ let(:values) { {book: Struct.new(:image_cover).new(val)} }
1672
+ let(:val) { "image" }
1673
+
1674
+ it "ignores value" do
1675
+ html = form_for("/books", values: values) do |f|
1676
+ f.file_field "book.image_cover"
1677
+ end
1678
+
1679
+ expect(html).to include %(<input type="file" name="book[image_cover]" id="book-image-cover">)
1680
+ end
1681
+ end
1682
+
1683
+ context "with filled params" do
1684
+ let(:params) { {book: {image_cover: val}} }
1685
+ let(:val) { "image" }
1686
+
1687
+ it "ignores value" do
1688
+ html = form_for("/books") do |f|
1689
+ f.file_field "book.image_cover"
1690
+ end
1691
+
1692
+ expect(html).to include %(<input type="file" name="book[image_cover]" id="book-image-cover">)
1693
+ end
1694
+ end
1695
+ end
1696
+
1697
+ describe "#hidden_field" do
1698
+ it "renders" do
1699
+ html = form_for("/books") do |f|
1700
+ f.hidden_field "book.author_id"
1701
+ end
1702
+
1703
+ expect(html).to include %(<input type="hidden" name="book[author_id]" id="book-author-id" value="">)
1704
+ end
1705
+
1706
+ it "allows to override 'id' attribute" do
1707
+ html = form_for("/books") do |f|
1708
+ f.hidden_field "book.author_id", id: "author-id"
1709
+ end
1710
+
1711
+ expect(html).to include %(<input type="hidden" name="book[author_id]" id="author-id" value="">)
1712
+ end
1713
+
1714
+ it "allows to override 'name' attribute" do
1715
+ html = form_for("/books") do |f|
1716
+ f.hidden_field "book.author_id", name: "book[author]"
1717
+ end
1718
+
1719
+ expect(html).to include %(<input type="hidden" name="book[author]" id="book-author-id" value="">)
1720
+ end
1721
+
1722
+ it "allows to override 'value' attribute" do
1723
+ html = form_for("/books") do |f|
1724
+ f.hidden_field "book.author_id", value: "23"
1725
+ end
1726
+
1727
+ expect(html).to include %(<input type="hidden" name="book[author_id]" id="book-author-id" value="23">)
1728
+ end
1729
+
1730
+ it "allows to specify HTML attributes" do
1731
+ html = form_for("/books") do |f|
1732
+ f.hidden_field "book.author_id", class: "form-details"
1733
+ end
1734
+
1735
+ expect(html).to include %(<input type="hidden" name="book[author_id]" id="book-author-id" value="" class="form-details">)
1736
+ end
1737
+
1738
+ context "with values" do
1739
+ let(:values) { {book: Struct.new(:author_id).new(val)} }
1740
+ let(:val) { "1" }
1741
+
1742
+ it "renders with value" do
1743
+ html = form_for("/books", values: values) do |f|
1744
+ f.hidden_field "book.author_id"
1745
+ end
1746
+
1747
+ expect(html).to include %(<input type="hidden" name="book[author_id]" id="book-author-id" value="1">)
1748
+ end
1749
+
1750
+ it "allows to override 'value' attribute" do
1751
+ html = form_for("/books", values: values) do |f|
1752
+ f.hidden_field "book.author_id", value: "23"
1753
+ end
1754
+
1755
+ expect(html).to include %(<input type="hidden" name="book[author_id]" id="book-author-id" value="23">)
1756
+ end
1757
+ end
1758
+
1759
+ context "with filled params" do
1760
+ let(:params) { {book: {author_id: val}} }
1761
+ let(:val) { "1" }
1762
+
1763
+ it "renders with value" do
1764
+ html = form_for("/books") do |f|
1765
+ f.hidden_field "book.author_id"
1766
+ end
1767
+
1768
+ expect(html).to include %(<input type="hidden" name="book[author_id]" id="book-author-id" value="1">)
1769
+ end
1770
+
1771
+ it "allows to override 'value' attribute" do
1772
+ html = form_for("/books") do |f|
1773
+ f.hidden_field "book.author_id", value: "23"
1774
+ end
1775
+
1776
+ expect(html).to include %(<input type="hidden" name="book[author_id]" id="book-author-id" value="23">)
1777
+ end
1778
+ end
1779
+ end
1780
+
1781
+ describe "#number_field" do
1782
+ it "renders the element" do
1783
+ html = form_for("/books") do |f|
1784
+ f.number_field "book.percent_read"
1785
+ end
1786
+
1787
+ expect(html).to include %(<input type="number" name="book[percent_read]" id="book-percent-read" value="">)
1788
+ end
1789
+
1790
+ it "allows to override 'id' attribute" do
1791
+ html = form_for("/books") do |f|
1792
+ f.number_field "book.percent_read", id: "percent-read"
1793
+ end
1794
+
1795
+ expect(html).to include %(<input type="number" name="book[percent_read]" id="percent-read" value="">)
1796
+ end
1797
+
1798
+ it "allows to override the 'name' attribute" do
1799
+ html = form_for("/books") do |f|
1800
+ f.number_field "book.percent_read", name: "book[read]"
1801
+ end
1802
+
1803
+ expect(html).to include %(<input type="number" name="book[read]" id="book-percent-read" value="">)
1804
+ end
1805
+
1806
+ it "allows to override the 'value' attribute" do
1807
+ html = form_for("/books") do |f|
1808
+ f.number_field "book.percent_read", value: "99"
1809
+ end
1810
+
1811
+ expect(html).to include %(<input type="number" name="book[percent_read]" id="book-percent-read" value="99">)
1812
+ end
1813
+
1814
+ it "allows to specify HTML attributes" do
1815
+ html = form_for("/books") do |f|
1816
+ f.number_field "book.percent_read", class: "form-control"
1817
+ end
1818
+
1819
+ expect(html).to include %(<input type="number" name="book[percent_read]" id="book-percent-read" value="" class="form-control">)
1820
+ end
1821
+
1822
+ it "allows to specify a 'min' attribute" do
1823
+ html = form_for("/books") do |f|
1824
+ f.number_field "book.percent_read", min: 0
1825
+ end
1826
+
1827
+ expect(html).to include %(<input type="number" name="book[percent_read]" id="book-percent-read" value="" min="0">)
1828
+ end
1829
+
1830
+ it "allows to specify a 'max' attribute" do
1831
+ html = form_for("/books") do |f|
1832
+ f.number_field "book.percent_read", max: 100
1833
+ end
1834
+
1835
+ expect(html).to include %(<input type="number" name="book[percent_read]" id="book-percent-read" value="" max="100">)
1836
+ end
1837
+
1838
+ it "allows to specify a 'step' attribute" do
1839
+ html = form_for("/books") do |f|
1840
+ f.number_field "book.percent_read", step: 5
1841
+ end
1842
+
1843
+ expect(html).to include %(<input type="number" name="book[percent_read]" id="book-percent-read" value="" step="5">)
1844
+ end
1845
+
1846
+ context "with values" do
1847
+ let(:values) { {book: Struct.new(:percent_read).new(val)} }
1848
+ let(:val) { 95 }
1849
+
1850
+ it "renders with value" do
1851
+ html = form_for("/books", values: values) do |f|
1852
+ f.number_field "book.percent_read"
1853
+ end
1854
+
1855
+ expect(html).to include %(<input type="number" name="book[percent_read]" id="book-percent-read" value="95">)
1856
+ end
1857
+
1858
+ it "allows to override 'value' attribute" do
1859
+ html = form_for("/books", values: values) do |f|
1860
+ f.number_field "book.percent_read", value: 50
1861
+ end
1862
+
1863
+ expect(html).to include %(<input type="number" name="book[percent_read]" id="book-percent-read" value="50">)
1864
+ end
1865
+ end
1866
+
1867
+ context "with filled params" do
1868
+ let(:params) { {book: {percent_read: val}} }
1869
+ let(:val) { 95 }
1870
+
1871
+ it "renders with value" do
1872
+ html = form_for("/books") do |f|
1873
+ f.number_field "book.percent_read"
1874
+ end
1875
+
1876
+ expect(html).to include %(<input type="number" name="book[percent_read]" id="book-percent-read" value="95">)
1877
+ end
1878
+
1879
+ it "allows to override 'value' attribute" do
1880
+ html = form_for("/books") do |f|
1881
+ f.number_field "book.percent_read", value: 50
1882
+ end
1883
+
1884
+ expect(html).to include %(<input type="number" name="book[percent_read]" id="book-percent-read" value="50">)
1885
+ end
1886
+ end
1887
+ end
1888
+
1889
+ describe "#range_field" do
1890
+ it "renders the element" do
1891
+ html = form_for("/books") do |f|
1892
+ f.range_field "book.discount_percentage"
1893
+ end
1894
+
1895
+ expect(html).to include %(<input type="range" name="book[discount_percentage]" id="book-discount-percentage" value="">)
1896
+ end
1897
+
1898
+ it "allows to override 'id' attribute" do
1899
+ html = form_for("/books") do |f|
1900
+ f.range_field "book.discount_percentage", id: "discount-percentage"
1901
+ end
1902
+
1903
+ expect(html).to include %(<input type="range" name="book[discount_percentage]" id="discount-percentage" value="">)
1904
+ end
1905
+
1906
+ it "allows to override the 'name' attribute" do
1907
+ html = form_for("/books") do |f|
1908
+ f.range_field "book.discount_percentage", name: "book[read]"
1909
+ end
1910
+
1911
+ expect(html).to include %(<input type="range" name="book[read]" id="book-discount-percentage" value="">)
1912
+ end
1913
+
1914
+ it "allows to override the 'value' attribute" do
1915
+ html = form_for("/books") do |f|
1916
+ f.range_field "book.discount_percentage", value: "99"
1917
+ end
1918
+
1919
+ expect(html).to include %(<input type="range" name="book[discount_percentage]" id="book-discount-percentage" value="99">)
1920
+ end
1921
+
1922
+ it "allows to specify HTML attributes" do
1923
+ html = form_for("/books") do |f|
1924
+ f.range_field "book.discount_percentage", class: "form-control"
1925
+ end
1926
+
1927
+ expect(html).to include %(<input type="range" name="book[discount_percentage]" id="book-discount-percentage" value="" class="form-control">)
1928
+ end
1929
+
1930
+ it "allows to specify a 'min' attribute" do
1931
+ html = form_for("/books") do |f|
1932
+ f.range_field "book.discount_percentage", min: 0
1933
+ end
1934
+
1935
+ expect(html).to include %(<input type="range" name="book[discount_percentage]" id="book-discount-percentage" value="" min="0">)
1936
+ end
1937
+
1938
+ it "allows to specify a 'max' attribute" do
1939
+ html = form_for("/books") do |f|
1940
+ f.range_field "book.discount_percentage", max: 100
1941
+ end
1942
+
1943
+ expect(html).to include %(<input type="range" name="book[discount_percentage]" id="book-discount-percentage" value="" max="100">)
1944
+ end
1945
+
1946
+ it "allows to specify a 'step' attribute" do
1947
+ html = form_for("/books") do |f|
1948
+ f.range_field "book.discount_percentage", step: 5
1949
+ end
1950
+
1951
+ expect(html).to include %(<input type="range" name="book[discount_percentage]" id="book-discount-percentage" value="" step="5">)
1952
+ end
1953
+
1954
+ context "with values" do
1955
+ let(:values) { {book: Struct.new(:discount_percentage).new(val)} }
1956
+ let(:val) { 95 }
1957
+
1958
+ it "renders with value" do
1959
+ html = form_for("/books", values: values) do |f|
1960
+ f.range_field "book.discount_percentage"
1961
+ end
1962
+
1963
+ expect(html).to include %(<input type="range" name="book[discount_percentage]" id="book-discount-percentage" value="95">)
1964
+ end
1965
+
1966
+ it "allows to override 'value' attribute" do
1967
+ html = form_for("/books", values: values) do |f|
1968
+ f.range_field "book.discount_percentage", value: 50
1969
+ end
1970
+
1971
+ expect(html).to include %(<input type="range" name="book[discount_percentage]" id="book-discount-percentage" value="50">)
1972
+ end
1973
+ end
1974
+
1975
+ context "with filled params" do
1976
+ let(:params) { {book: {discount_percentage: val}} }
1977
+ let(:val) { 95 }
1978
+
1979
+ it "renders with value" do
1980
+ html = form_for("/books") do |f|
1981
+ f.range_field "book.discount_percentage"
1982
+ end
1983
+
1984
+ expect(html).to include %(<input type="range" name="book[discount_percentage]" id="book-discount-percentage" value="95">)
1985
+ end
1986
+
1987
+ it "allows to override 'value' attribute" do
1988
+ html = form_for("/books") do |f|
1989
+ f.range_field "book.discount_percentage", value: 50
1990
+ end
1991
+
1992
+ expect(html).to include %(<input type="range" name="book[discount_percentage]" id="book-discount-percentage" value="50">)
1993
+ end
1994
+ end
1995
+ end
1996
+
1997
+ describe "#text_area" do
1998
+ it "renders the element" do
1999
+ html = form_for("/books") do |f|
2000
+ f.text_area "book.description"
2001
+ end
2002
+
2003
+ expect(html).to include %(<textarea name="book[description]" id="book-description">\n</textarea>)
2004
+ end
2005
+
2006
+ it "allows to override 'id' attribute" do
2007
+ html = form_for("/books") do |f|
2008
+ f.text_area "book.description", nil, id: "desc"
2009
+ end
2010
+
2011
+ expect(html).to include %(<textarea name="book[description]" id="desc">\n</textarea>)
2012
+ end
2013
+
2014
+ it "allows to override 'name' attribute" do
2015
+ html = form_for("/books") do |f|
2016
+ f.text_area "book.description", nil, name: "book[desc]"
2017
+ end
2018
+
2019
+ expect(html).to include %(<textarea name="book[desc]" id="book-description">\n</textarea>)
2020
+ end
2021
+
2022
+ it "allows to specify HTML attributes" do
2023
+ html = form_for("/books") do |f|
2024
+ f.text_area "book.description", nil, class: "form-control", cols: "5"
2025
+ end
2026
+
2027
+ expect(html).to include %(<textarea name="book[description]" id="book-description" class="form-control" cols="5">\n</textarea>)
2028
+ end
2029
+
2030
+ it "allows to omit content" do
2031
+ html = form_for("/books") do |f|
2032
+ f.text_area "book.description", class: "form-control", cols: "5"
2033
+ end
2034
+
2035
+ expect(html).to include %(<textarea name="book[description]" id="book-description" class="form-control" cols="5">\n</textarea>)
2036
+ end
2037
+
2038
+ it "allows to omit content, by accepting Hash serializable options" do
2039
+ options = {class: "form-control", cols: 5}
2040
+
2041
+ html = form_for("/books") do |f|
2042
+ f.text_area "book.description", options
2043
+ end
2044
+
2045
+ expect(html).to include %(<textarea name="book[description]" id="book-description" class="form-control" cols="5">\n</textarea>)
2046
+ end
2047
+
2048
+ context "set content explicitly" do
2049
+ let(:content) { "A short description of the book" }
2050
+
2051
+ it "allows to set content" do
2052
+ html = form_for("/books") do |f|
2053
+ f.text_area "book.description", content
2054
+ end
2055
+
2056
+ expect(html).to include %(<textarea name="book[description]" id="book-description">\n#{content}</textarea>)
2057
+ end
2058
+ end
2059
+
2060
+ context "with values" do
2061
+ let(:values) { {book: Struct.new(:description).new(val)} }
2062
+ let(:val) { "A short description of the book" }
2063
+
2064
+ it "renders with value" do
2065
+ html = form_for("/books", values: values) do |f|
2066
+ f.text_area "book.description"
2067
+ end
2068
+
2069
+ expect(html).to include %(<textarea name="book[description]" id="book-description">\n#{val}</textarea>)
2070
+ end
2071
+
2072
+ it "renders with value, when only attributes are specified" do
2073
+ html = form_for("/books", values: values) do |f|
2074
+ f.text_area "book.description", class: "form-control"
2075
+ end
2076
+
2077
+ expect(html).to include %(<textarea name="book[description]" id="book-description" class="form-control">\n#{val}</textarea>)
2078
+ end
2079
+
2080
+ it "allows to override value" do
2081
+ html = form_for("/books", values: values) do |f|
2082
+ f.text_area "book.description", "Just a simple description"
2083
+ end
2084
+
2085
+ expect(html).to include %(<textarea name="book[description]" id="book-description">\nJust a simple description</textarea>)
2086
+ end
2087
+
2088
+ it "forces blank value" do
2089
+ html = form_for("/books", values: values) do |f|
2090
+ f.text_area "book.description", ""
2091
+ end
2092
+
2093
+ expect(html).to include %(<textarea name="book[description]" id="book-description">\n</textarea>)
2094
+ end
2095
+
2096
+ it "forces blank value, when also attributes are specified" do
2097
+ html = form_for("/books", values: values) do |f|
2098
+ f.text_area "book.description", "", class: "form-control"
2099
+ end
2100
+
2101
+ expect(html).to include %(<textarea name="book[description]" id="book-description" class="form-control">\n</textarea>)
2102
+ end
2103
+ end
2104
+
2105
+ context "with filled params" do
2106
+ let(:params) { {book: {description: val}} }
2107
+ let(:val) { "A short description of the book" }
2108
+
2109
+ it "renders with value" do
2110
+ html = form_for("/books") do |f|
2111
+ f.text_area "book.description"
2112
+ end
2113
+
2114
+ expect(html).to include %(<textarea name="book[description]" id="book-description">\n#{val}</textarea>)
2115
+ end
2116
+
2117
+ it "renders with value, when only attributes are specified" do
2118
+ html = form_for("/books") do |f|
2119
+ f.text_area "book.description", class: "form-control"
2120
+ end
2121
+
2122
+ expect(html).to include %(<textarea name="book[description]" id="book-description" class="form-control">\n#{val}</textarea>)
2123
+ end
2124
+
2125
+ it "allows to override value" do
2126
+ html = form_for("/books") do |f|
2127
+ f.text_area "book.description", "Just a simple description"
2128
+ end
2129
+
2130
+ expect(html).to include %(<textarea name="book[description]" id="book-description">\nJust a simple description</textarea>)
2131
+ end
2132
+
2133
+ it "forces blank value" do
2134
+ html = form_for("/books") do |f|
2135
+ f.text_area "book.description", ""
2136
+ end
2137
+
2138
+ expect(html).to include %(<textarea name="book[description]" id="book-description">\n</textarea>)
2139
+ end
2140
+
2141
+ it "forces blank value, when also attributes are specified" do
2142
+ html = form_for("/books") do |f|
2143
+ f.text_area "book.description", "", class: "form-control"
2144
+ end
2145
+
2146
+ expect(html).to include %(<textarea name="book[description]" id="book-description" class="form-control">\n</textarea>)
2147
+ end
2148
+ end
2149
+ end
2150
+
2151
+ describe "#text_field" do
2152
+ it "renders" do
2153
+ html = form_for("/books") do |f|
2154
+ f.text_field "book.title"
2155
+ end
2156
+
2157
+ expect(html).to include %(<input type="text" name="book[title]" id="book-title" value="">)
2158
+ end
2159
+
2160
+ it "allows to override 'id' attribute" do
2161
+ html = form_for("/books") do |f|
2162
+ f.text_field "book.title", id: "book-short-title"
2163
+ end
2164
+
2165
+ expect(html).to include %(<input type="text" name="book[title]" id="book-short-title" value="">)
2166
+ end
2167
+
2168
+ it "allows to override 'name' attribute" do
2169
+ html = form_for("/books") do |f|
2170
+ f.text_field "book.title", name: "book[short_title]"
2171
+ end
2172
+
2173
+ expect(html).to include %(<input type="text" name="book[short_title]" id="book-title" value="">)
2174
+ end
2175
+
2176
+ it "allows to override 'value' attribute" do
2177
+ html = form_for("/books") do |f|
2178
+ f.text_field "book.title", value: "Refactoring"
2179
+ end
2180
+
2181
+ expect(html).to include %(<input type="text" name="book[title]" id="book-title" value="Refactoring">)
2182
+ end
2183
+
2184
+ it "allows to specify HTML attributes" do
2185
+ html = form_for("/books") do |f|
2186
+ f.text_field "book.title", class: "form-control"
2187
+ end
2188
+
2189
+ expect(html).to include %(<input type="text" name="book[title]" id="book-title" value="" class="form-control">)
2190
+ end
2191
+
2192
+ context "with values" do
2193
+ let(:values) { {book: Struct.new(:title).new(val)} }
2194
+ let(:val) { "Learn some <html>!" }
2195
+
2196
+ it "renders with value" do
2197
+ html = form_for("/books", values: values) do |f|
2198
+ f.text_field "book.title"
2199
+ end
2200
+
2201
+ expect(html).to include %(<input type="text" name="book[title]" id="book-title" value="Learn some &lt;html&gt;!">)
2202
+ end
2203
+
2204
+ it "allows to override 'value' attribute" do
2205
+ html = form_for("/books", values: values) do |f|
2206
+ f.text_field "book.title", value: "DDD"
2207
+ end
2208
+
2209
+ expect(html).to include %(<input type="text" name="book[title]" id="book-title" value="DDD">)
2210
+ end
2211
+ end
2212
+
2213
+ context "with filled params" do
2214
+ let(:params) { {book: {title: val}} }
2215
+ let(:val) { "Learn some <html>!" }
2216
+
2217
+ it "renders with value" do
2218
+ html = form_for("/books") do |f|
2219
+ f.text_field "book.title"
2220
+ end
2221
+
2222
+ expect(html).to include %(<input type="text" name="book[title]" id="book-title" value="Learn some &lt;html&gt;!">)
2223
+ end
2224
+
2225
+ it "allows to override 'value' attribute" do
2226
+ html = form_for("/books") do |f|
2227
+ f.text_field "book.title", value: "DDD"
2228
+ end
2229
+
2230
+ expect(html).to include %(<input type="text" name="book[title]" id="book-title" value="DDD">)
2231
+ end
2232
+ end
2233
+ end
2234
+
2235
+ describe "#search_field" do
2236
+ it "renders" do
2237
+ html = form_for("/books") do |f|
2238
+ f.search_field "book.search_title"
2239
+ end
2240
+
2241
+ expect(html).to include %(<input type="search" name="book[search_title]" id="book-search-title" value="">)
2242
+ end
2243
+
2244
+ it "allows to override 'id' attribute" do
2245
+ html = form_for("/books") do |f|
2246
+ f.search_field "book.search_title", id: "book-short-title"
2247
+ end
2248
+
2249
+ expect(html).to include %(<input type="search" name="book[search_title]" id="book-short-title" value="">)
2250
+ end
2251
+
2252
+ it "allows to override 'name' attribute" do
2253
+ html = form_for("/books") do |f|
2254
+ f.search_field "book.search_title", name: "book[short_title]"
2255
+ end
2256
+
2257
+ expect(html).to include %(<input type="search" name="book[short_title]" id="book-search-title" value="">)
2258
+ end
2259
+
2260
+ it "allows to override 'value' attribute" do
2261
+ html = form_for("/books") do |f|
2262
+ f.search_field "book.search_title", value: "Refactoring"
2263
+ end
2264
+
2265
+ expect(html).to include %(<input type="search" name="book[search_title]" id="book-search-title" value="Refactoring">)
2266
+ end
2267
+
2268
+ it "allows to specify HTML attributes" do
2269
+ html = form_for("/books") do |f|
2270
+ f.search_field "book.search_title", class: "form-control"
2271
+ end
2272
+
2273
+ expect(html).to include %(<input type="search" name="book[search_title]" id="book-search-title" value="" class="form-control">)
2274
+ end
2275
+
2276
+ context "with values" do
2277
+ let(:values) { {book: Struct.new(:search_title).new(val)} }
2278
+ let(:val) { "PPoEA" }
2279
+
2280
+ it "renders with value" do
2281
+ html = form_for("/books", values: values) do |f|
2282
+ f.search_field "book.search_title"
2283
+ end
2284
+
2285
+ expect(html).to include %(<input type="search" name="book[search_title]" id="book-search-title" value="PPoEA">)
2286
+ end
2287
+
2288
+ it "allows to override 'value' attribute" do
2289
+ html = form_for("/books", values: values) do |f|
2290
+ f.search_field "book.search_title", value: "DDD"
2291
+ end
2292
+
2293
+ expect(html).to include %(<input type="search" name="book[search_title]" id="book-search-title" value="DDD">)
2294
+ end
2295
+ end
2296
+
2297
+ context "with filled params" do
2298
+ let(:params) { {book: {search_title: val}} }
2299
+ let(:val) { "PPoEA" }
2300
+
2301
+ it "renders with value" do
2302
+ html = form_for("/books") do |f|
2303
+ f.search_field "book.search_title"
2304
+ end
2305
+
2306
+ expect(html).to include %(<input type="search" name="book[search_title]" id="book-search-title" value="PPoEA">)
2307
+ end
2308
+
2309
+ it "allows to override 'value' attribute" do
2310
+ html = form_for("/books") do |f|
2311
+ f.search_field "book.search_title", value: "DDD"
2312
+ end
2313
+
2314
+ expect(html).to include %(<input type="search" name="book[search_title]" id="book-search-title" value="DDD">)
2315
+ end
2316
+ end
2317
+ end
2318
+
2319
+ describe "#password_field" do
2320
+ it "renders" do
2321
+ html = form_for("/books") do |f|
2322
+ f.password_field "signup.password"
2323
+ end
2324
+
2325
+ expect(html).to include %(<input type="password" name="signup[password]" id="signup-password" value="">)
2326
+ end
2327
+
2328
+ it "allows to override 'id' attribute" do
2329
+ html = form_for("/books") do |f|
2330
+ f.password_field "signup.password", id: "signup-pass"
2331
+ end
2332
+
2333
+ expect(html).to include %(<input type="password" name="signup[password]" id="signup-pass" value="">)
2334
+ end
2335
+
2336
+ it "allows to override 'name' attribute" do
2337
+ html = form_for("/books") do |f|
2338
+ f.password_field "signup.password", name: "password"
2339
+ end
2340
+
2341
+ expect(html).to include %(<input type="password" name="password" id="signup-password" value="">)
2342
+ end
2343
+
2344
+ it "allows to override 'value' attribute" do
2345
+ html = form_for("/books") do |f|
2346
+ f.password_field "signup.password", value: "topsecret"
2347
+ end
2348
+
2349
+ expect(html).to include %(<input type="password" name="signup[password]" id="signup-password" value="topsecret">)
2350
+ end
2351
+
2352
+ it "allows to specify HTML attributes" do
2353
+ html = form_for("/books") do |f|
2354
+ f.password_field "signup.password", class: "form-control"
2355
+ end
2356
+
2357
+ expect(html).to include %(<input type="password" name="signup[password]" id="signup-password" value="" class="form-control">)
2358
+ end
2359
+
2360
+ context "with values" do
2361
+ let(:values) { {signup: Struct.new(:password).new(val)} }
2362
+ let(:val) { "secret" }
2363
+
2364
+ it "ignores value" do
2365
+ html = form_for("/books", values: values) do |f|
2366
+ f.password_field "signup.password"
2367
+ end
2368
+
2369
+ expect(html).to include %(<input type="password" name="signup[password]" id="signup-password" value="">)
2370
+ end
2371
+
2372
+ it "allows to override 'value' attribute" do
2373
+ html = form_for("/books", values: values) do |f|
2374
+ f.password_field "signup.password", value: "123"
2375
+ end
2376
+
2377
+ expect(html).to include %(<input type="password" name="signup[password]" id="signup-password" value="123">)
2378
+ end
2379
+ end
2380
+
2381
+ context "with filled params" do
2382
+ let(:params) { {signup: {password: val}} }
2383
+ let(:val) { "secret" }
2384
+
2385
+ it "ignores value" do
2386
+ html = form_for("/books") do |f|
2387
+ f.password_field "signup.password"
2388
+ end
2389
+
2390
+ expect(html).to include %(<input type="password" name="signup[password]" id="signup-password" value="">)
2391
+ end
2392
+
2393
+ it "allows to override 'value' attribute" do
2394
+ html = form_for("/books") do |f|
2395
+ f.password_field "signup.password", value: "123"
2396
+ end
2397
+
2398
+ expect(html).to include %(<input type="password" name="signup[password]" id="signup-password" value="123">)
2399
+ end
2400
+ end
2401
+ end
2402
+
2403
+ describe "#radio_button" do
2404
+ it "renders" do
2405
+ html = form_for("/books") do |f|
2406
+ f.radio_button("book.category", "Fiction") +
2407
+ f.radio_button("book.category", "Non-Fiction")
2408
+ end
2409
+
2410
+ expect(html).to include %(<input type="radio" name="book[category]" value="Fiction"><input type="radio" name="book[category]" value="Non-Fiction">)
2411
+ end
2412
+
2413
+ it "allows to override 'name' attribute" do
2414
+ html = form_for("/books") do |f|
2415
+ f.radio_button("book.category", "Fiction", name: "category_name") +
2416
+ f.radio_button("book.category", "Non-Fiction", name: "category_name")
2417
+ end
2418
+
2419
+ expect(html).to include %(<input type="radio" name="category_name" value="Fiction"><input type="radio" name="category_name" value="Non-Fiction">)
2420
+ end
2421
+
2422
+ it "allows to specify HTML attributes" do
2423
+ html = form_for("/books") do |f|
2424
+ f.radio_button("book.category", "Fiction", class: "form-control") +
2425
+ f.radio_button("book.category", "Non-Fiction", class: "radio-button")
2426
+ end
2427
+
2428
+ expect(html).to include %(<input type="radio" name="book[category]" value="Fiction" class="form-control"><input type="radio" name="book[category]" value="Non-Fiction" class="radio-button">)
2429
+ end
2430
+
2431
+ context "with values" do
2432
+ let(:values) { {book: Struct.new(:category).new(val)} }
2433
+ let(:val) { "Non-Fiction" }
2434
+
2435
+ it "renders with value" do
2436
+ html = form_for("/books", values: values) do |f|
2437
+ f.radio_button("book.category", "Fiction") +
2438
+ f.radio_button("book.category", "Non-Fiction")
2439
+ end
2440
+
2441
+ expect(html).to include %(<input type="radio" name="book[category]" value="Fiction"><input type="radio" name="book[category]" value="Non-Fiction" checked="checked">)
2442
+ end
2443
+ end
2444
+
2445
+ context "with filled params" do
2446
+ context "string value" do
2447
+ let(:params) { {book: {category: val}} }
2448
+ let(:val) { "Non-Fiction" }
2449
+
2450
+ it "renders with value" do
2451
+ html = form_for("/books") do |f|
2452
+ f.radio_button("book.category", "Fiction") +
2453
+ f.radio_button("book.category", "Non-Fiction")
2454
+ end
2455
+
2456
+ expect(html).to include %(<input type="radio" name="book[category]" value="Fiction"><input type="radio" name="book[category]" value="Non-Fiction" checked="checked">)
2457
+ end
2458
+ end
2459
+
2460
+ context "decimal value" do
2461
+ let(:params) { {book: {price: val}} }
2462
+ let(:val) { "20.0" }
2463
+
2464
+ it "renders with value" do
2465
+ html = form_for("/books") do |f|
2466
+ f.radio_button("book.price", 10.0) +
2467
+ f.radio_button("book.price", 20.0)
2468
+ end
2469
+
2470
+ expect(html).to include %(<input type="radio" name="book[price]" value="10.0"><input type="radio" name="book[price]" value="20.0" checked="checked">)
2471
+ end
2472
+ end
2473
+ end
2474
+ end
2475
+
2476
+ describe "#select" do
2477
+ let(:option_values) { {"Italy" => "it", "United States" => "us"} }
2478
+
2479
+ it "renders" do
2480
+ html = form_for("/books") do |f|
2481
+ f.select "book.store", option_values
2482
+ end
2483
+
2484
+ expect(html).to include %(<select name="book[store]" id="book-store"><option value="it">Italy</option><option value="us">United States</option></select>)
2485
+ end
2486
+
2487
+ it "allows to override 'id' attribute" do
2488
+ html = form_for("/books") do |f|
2489
+ f.select "book.store", option_values, id: "store"
2490
+ end
2491
+
2492
+ expect(html).to include %(<select name="book[store]" id="store"><option value="it">Italy</option><option value="us">United States</option></select>)
2493
+ end
2494
+
2495
+ it "allows to override 'name' attribute" do
2496
+ html = form_for("/books") do |f|
2497
+ f.select "book.store", option_values, name: "store"
2498
+ end
2499
+
2500
+ expect(html).to include %(<select name="store" id="book-store"><option value="it">Italy</option><option value="us">United States</option></select>)
2501
+ end
2502
+
2503
+ it "allows to specify HTML attributes" do
2504
+ html = form_for("/books") do |f|
2505
+ f.select "book.store", option_values, class: "form-control"
2506
+ end
2507
+
2508
+ expect(html).to include %(<select name="book[store]" id="book-store" class="form-control"><option value="it">Italy</option><option value="us">United States</option></select>)
2509
+ end
2510
+
2511
+ it "allows to specify HTML attributes for options" do
2512
+ html = form_for("/books") do |f|
2513
+ f.select "book.store", option_values, options: {class: "form-option"}
2514
+ end
2515
+
2516
+ expect(html).to include %(<select name="book[store]" id="book-store"><option value="it" class="form-option">Italy</option><option value="us" class="form-option">United States</option></select>)
2517
+ end
2518
+
2519
+ context "with option 'multiple'" do
2520
+ it "renders" do
2521
+ html = form_for("/books") do |f|
2522
+ f.select "book.store", option_values, multiple: true
2523
+ end
2524
+
2525
+ expect(html).to include %(<select name="book[store][]" id="book-store" multiple="multiple"><option value="it">Italy</option><option value="us">United States</option></select>)
2526
+ end
2527
+
2528
+ it "allows to select values" do
2529
+ html = form_for("/books") do |f|
2530
+ f.select "book.store", option_values, multiple: true, options: {selected: %w[it us]}
2531
+ end
2532
+
2533
+ expect(html).to include %(<select name="book[store][]" id="book-store" multiple="multiple"><option value="it" selected="selected">Italy</option><option value="us" selected="selected">United States</option></select>)
2534
+ end
2535
+ end
2536
+
2537
+ context "with values an structured Array of values" do
2538
+ let(:option_values) { [%w[Italy it], ["United States", "us"]] }
2539
+
2540
+ it "renders" do
2541
+ html = form_for("/books") do |f|
2542
+ f.select "book.store", option_values
2543
+ end
2544
+
2545
+ expect(html).to include %(<select name="book[store]" id="book-store"><option value="it">Italy</option><option value="us">United States</option></select>)
2546
+ end
2547
+
2548
+ context "and filled params" do
2549
+ let(:params) { {book: {store: val}} }
2550
+ let(:val) { "it" }
2551
+
2552
+ it "renders with value" do
2553
+ html = form_for("/books") do |f|
2554
+ f.select "book.store", option_values
2555
+ end
2556
+
2557
+ expect(html).to include %(<select name="book[store]" id="book-store"><option value="it" selected="selected">Italy</option><option value="us">United States</option></select>)
2558
+ end
2559
+ end
2560
+
2561
+ context "and repeated values" do
2562
+ let(:option_values) { [%w[Italy it], ["United States", "us"], %w[Italy it]] }
2563
+
2564
+ it "renders" do
2565
+ html = form_for("/books") do |f|
2566
+ f.select "book.store", option_values
2567
+ end
2568
+
2569
+ expect(html).to include %(<select name="book[store]" id="book-store"><option value="it">Italy</option><option value="us">United States</option><option value="it">Italy</option></select>)
2570
+ end
2571
+ end
2572
+ end
2573
+
2574
+ context "with values an Array of objects" do
2575
+ let(:values) { [Store.new("it", "Italy"), Store.new("us", "United States")] }
2576
+
2577
+ it "renders" do
2578
+ html = form_for("/books") do |f|
2579
+ f.select "book.store", option_values
2580
+ end
2581
+
2582
+ expect(html).to include %(<select name="book[store]" id="book-store"><option value="it">Italy</option><option value="us">United States</option></select>)
2583
+ end
2584
+
2585
+ context "and filled params" do
2586
+ let(:params) { {book: {store: val}} }
2587
+ let(:val) { "it" }
2588
+
2589
+ it "renders with value" do
2590
+ html = form_for("/books") do |f|
2591
+ f.select "book.store", option_values
2592
+ end
2593
+
2594
+ expect(html).to include %(<select name="book[store]" id="book-store"><option value="it" selected="selected">Italy</option><option value="us">United States</option></select>)
2595
+ end
2596
+ end
2597
+ end
2598
+
2599
+ context "with values" do
2600
+ let(:values) { {book: Struct.new(:store).new(val)} }
2601
+ let(:val) { "it" }
2602
+
2603
+ it "renders with value" do
2604
+ html = form_for("/books", values: values) do |f|
2605
+ f.select "book.store", option_values
2606
+ end
2607
+
2608
+ expect(html).to include %(<select name="book[store]" id="book-store"><option value="it" selected="selected">Italy</option><option value="us">United States</option></select>)
2609
+ end
2610
+ end
2611
+
2612
+ context "with filled params" do
2613
+ let(:params) { {book: {store: val}} }
2614
+ let(:val) { "it" }
2615
+
2616
+ it "renders with value" do
2617
+ html = form_for("/books") do |f|
2618
+ f.select "book.store", option_values
2619
+ end
2620
+
2621
+ expect(html).to include %(<select name="book[store]" id="book-store"><option value="it" selected="selected">Italy</option><option value="us">United States</option></select>)
2622
+ end
2623
+ end
2624
+
2625
+ context "with prompt option" do
2626
+ it "allows string" do
2627
+ html = form_for("/books") do |f|
2628
+ f.select "book.store", option_values, options: {prompt: "Select a store"}
2629
+ end
2630
+
2631
+ expect(html).to include %(<select name="book[store]" id="book-store"><option disabled="disabled">Select a store</option><option value="it">Italy</option><option value="us">United States</option></select>)
2632
+ end
2633
+
2634
+ it "allows blank string" do
2635
+ html = form_for("/books") do |f|
2636
+ f.select "book.store", option_values, options: {prompt: ""}
2637
+ end
2638
+
2639
+ expect(html).to include %(<select name="book[store]" id="book-store"><option disabled="disabled"></option><option value="it">Italy</option><option value="us">United States</option></select>)
2640
+ end
2641
+
2642
+ context "with values" do
2643
+ let(:values) { {book: Struct.new(:store).new(val)} }
2644
+ let(:val) { "it" }
2645
+
2646
+ it "renders with value" do
2647
+ html = form_for("/books", values: values) do |f|
2648
+ f.select "book.store", option_values, options: {prompt: "Select a store"}
2649
+ end
2650
+
2651
+ expect(html).to include %(<select name="book[store]" id="book-store"><option disabled="disabled">Select a store</option><option value="it" selected="selected">Italy</option><option value="us">United States</option></select>)
2652
+ end
2653
+ end
2654
+
2655
+ context "with filled params" do
2656
+ context "string values" do
2657
+ let(:params) { {book: {store: val}} }
2658
+ let(:val) { "it" }
2659
+
2660
+ it "renders with value" do
2661
+ html = form_for("/books") do |f|
2662
+ f.select "book.store", option_values, options: {prompt: "Select a store"}
2663
+ end
2664
+
2665
+ expect(html).to include %(<select name="book[store]" id="book-store"><option disabled="disabled">Select a store</option><option value="it" selected="selected">Italy</option><option value="us">United States</option></select>)
2666
+ end
2667
+ end
2668
+
2669
+ context "integer values" do
2670
+ let(:values) { {"Brave new world" => 1, "Solaris" => 2} }
2671
+ let(:params) { {bookshelf: {book: val}} }
2672
+ let(:val) { "1" }
2673
+
2674
+ it "renders" do
2675
+ html = form_for("/books") do |f|
2676
+ f.select "bookshelf.book", values
2677
+ end
2678
+
2679
+ expect(html).to include %(<select name="bookshelf[book]" id="bookshelf-book"><option value="1" selected="selected">Brave new world</option><option value="2">Solaris</option></select>)
2680
+ end
2681
+ end
2682
+ end
2683
+ end
2684
+
2685
+ context "with selected attribute" do
2686
+ let(:params) { {book: {store: val}} }
2687
+ let(:val) { "it" }
2688
+
2689
+ it "sets the selected attribute" do
2690
+ html = form_for("/books") do |f|
2691
+ f.select "book.store", option_values, options: {selected: val}
2692
+ end
2693
+
2694
+ expect(html).to include %(<select name="book[store]" id="book-store"><option value="it" selected="selected">Italy</option><option value="us">United States</option></select>)
2695
+ end
2696
+ end
2697
+
2698
+ context "with nil as a value" do
2699
+ let(:option_values) { {"Italy" => "it", "United States" => "us", "N/A" => nil} }
2700
+
2701
+ it "sets nil option as selected by default" do
2702
+ html = form_for("/books") do |f|
2703
+ f.select "book.store", option_values
2704
+ end
2705
+
2706
+ expect(html).to include %(<select name="book[store]" id="book-store"><option value="it">Italy</option><option value="us">United States</option><option selected="selected">N/A</option></select>)
2707
+ end
2708
+
2709
+ it "set as selected the option with nil value" do
2710
+ html = form_for("/books") do |f|
2711
+ f.select "book.store", option_values, options: {selected: nil}
2712
+ end
2713
+
2714
+ expect(html).to include %(<select name="book[store]" id="book-store"><option value="it">Italy</option><option value="us">United States</option><option selected="selected">N/A</option></select>)
2715
+ end
2716
+
2717
+ it "set as selected the option with a value" do
2718
+ html = form_for("/books") do |f|
2719
+ f.select "book.store", option_values, options: {selected: "it"}
2720
+ end
2721
+
2722
+ expect(html).to include %(<select name="book[store]" id="book-store"><option value="it" selected="selected">Italy</option><option value="us">United States</option><option>N/A</option></select>)
2723
+ end
2724
+
2725
+ it "allows to force the selection of none" do
2726
+ html = form_for("/books") do |f|
2727
+ f.select "book.store", option_values, options: {selected: "none"}
2728
+ end
2729
+
2730
+ expect(html).to include %(<select name="book[store]" id="book-store"><option value="it">Italy</option><option value="us">United States</option><option>N/A</option></select>)
2731
+ end
2732
+
2733
+ context "with values" do
2734
+ let(:values) { {book: Struct.new(:category).new(val)} }
2735
+ let(:option_values) { {"N/A" => nil, "Horror" => "horror", "SciFy" => "scify"} }
2736
+ let(:val) { "horror" }
2737
+
2738
+ it "sets correct value as selected" do
2739
+ html = form_for("/books", values: values) do |f|
2740
+ f.select "book.category", option_values
2741
+ end
2742
+
2743
+ expect(html).to include %(<form action="/books" accept-charset="utf-8" method="POST"><select name="book[category]" id="book-category"><option>N/A</option><option value="horror" selected="selected">Horror</option><option value="scify">SciFy</option></select></form>)
2744
+ end
2745
+ end
2746
+
2747
+ context "with non String values" do
2748
+ let(:values) { {book: Struct.new(:category).new(val)} }
2749
+ let(:option_values) { {"Horror" => "1", "SciFy" => "2"} }
2750
+ let(:val) { 1 }
2751
+
2752
+ it "sets correct value as selected" do
2753
+ html = form_for("/books", values: values) do |f|
2754
+ f.select "book.category", option_values
2755
+ end
2756
+
2757
+ expect(html).to include %(<form action="/books" accept-charset="utf-8" method="POST"><select name="book[category]" id="book-category"><option value="1" selected="selected">Horror</option><option value="2">SciFy</option></select></form>)
2758
+ end
2759
+ end
2760
+ end
2761
+ end
2762
+
2763
+ describe "#datalist" do
2764
+ let(:values) { ["Italy", "United States"] }
2765
+
2766
+ it "renders" do
2767
+ html = form_for("/books") do |f|
2768
+ f.datalist "book.store", values, "books"
2769
+ end
2770
+
2771
+ expect(html).to include %(<input type="text" name="book[store]" id="book-store" value="" list="books"><datalist id="books"><option value="Italy"></option><option value="United States"></option></datalist>)
2772
+ end
2773
+
2774
+ it "just allows to override 'id' attribute of the text input" do
2775
+ html = form_for("/books") do |f|
2776
+ f.datalist "book.store", values, "books", id: "store"
2777
+ end
2778
+
2779
+ expect(html).to include %(<input type="text" name="book[store]" id="store" value="" list="books"><datalist id="books"><option value="Italy"></option><option value="United States"></option></datalist>)
2780
+ end
2781
+
2782
+ it "allows to override 'name' attribute" do
2783
+ html = form_for("/books") do |f|
2784
+ f.datalist "book.store", values, "books", name: "store"
2785
+ end
2786
+
2787
+ expect(html).to include %(<input type="text" name="store" id="book-store" value="" list="books"><datalist id="books"><option value="Italy"></option><option value="United States"></option></datalist>)
2788
+ end
2789
+
2790
+ it "allows to specify HTML attributes" do
2791
+ html = form_for("/books") do |f|
2792
+ f.datalist "book.store", values, "books", class: "form-control"
2793
+ end
2794
+
2795
+ expect(html).to include %(<input type="text" name="book[store]" id="book-store" value="" class="form-control" list="books"><datalist id="books"><option value="Italy"></option><option value="United States"></option></datalist>)
2796
+ end
2797
+
2798
+ it "allows to specify HTML attributes for options" do
2799
+ html = form_for("/books") do |f|
2800
+ f.datalist "book.store", values, "books", options: {class: "form-option"}
2801
+ end
2802
+
2803
+ expect(html).to include %(<input type="text" name="book[store]" id="book-store" value="" list="books"><datalist id="books"><option value="Italy" class="form-option"></option><option value="United States" class="form-option"></option></datalist>)
2804
+ end
2805
+
2806
+ it "allows to specify HTML attributes for datalist" do
2807
+ html = form_for("/books") do |f|
2808
+ f.datalist "book.store", values, "books", datalist: {class: "form-option"}
2809
+ end
2810
+
2811
+ expect(html).to include %(<input type="text" name="book[store]" id="book-store" value="" list="books"><datalist class="form-option" id="books"><option value="Italy"></option><option value="United States"></option></datalist>)
2812
+ end
2813
+
2814
+ context "with a Hash of values" do
2815
+ let(:values) { {"Italy" => "it", "United States" => "us"} }
2816
+
2817
+ it "renders" do
2818
+ html = form_for("/books") do |f|
2819
+ f.datalist "book.store", values, "books"
2820
+ end
2821
+
2822
+ expect(html).to include %(<input type="text" name="book[store]" id="book-store" value="" list="books"><datalist id="books"><option value="Italy">it</option><option value="United States">us</option></datalist>)
2823
+ end
2824
+ end
2825
+ end
2826
+ end