hanami 2.0.3 → 2.1.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +37 -2
  3. data/LICENSE.md +1 -1
  4. data/README.md +26 -10
  5. data/hanami.gemspec +2 -2
  6. data/lib/hanami/app.rb +5 -0
  7. data/lib/hanami/config/actions.rb +4 -11
  8. data/lib/hanami/config/assets.rb +84 -0
  9. data/lib/hanami/config/null_config.rb +3 -0
  10. data/lib/hanami/config/views.rb +0 -4
  11. data/lib/hanami/config.rb +71 -5
  12. data/lib/hanami/extensions/action/slice_configured_action.rb +15 -7
  13. data/lib/hanami/extensions/action.rb +8 -6
  14. data/lib/hanami/extensions/router/errors.rb +58 -0
  15. data/lib/hanami/extensions/view/context.rb +129 -60
  16. data/lib/hanami/extensions/view/part.rb +26 -0
  17. data/lib/hanami/extensions/view/scope.rb +26 -0
  18. data/lib/hanami/extensions/view/slice_configured_context.rb +0 -2
  19. data/lib/hanami/extensions/view/slice_configured_helpers.rb +44 -0
  20. data/lib/hanami/extensions/view/slice_configured_view.rb +106 -21
  21. data/lib/hanami/extensions/view/standard_helpers.rb +18 -0
  22. data/lib/hanami/extensions.rb +10 -3
  23. data/lib/hanami/helpers/assets_helper.rb +752 -0
  24. data/lib/hanami/helpers/form_helper/form_builder.rb +1391 -0
  25. data/lib/hanami/helpers/form_helper/values.rb +75 -0
  26. data/lib/hanami/helpers/form_helper.rb +213 -0
  27. data/lib/hanami/middleware/assets.rb +21 -0
  28. data/lib/hanami/middleware/public_errors_app.rb +75 -0
  29. data/lib/hanami/middleware/render_errors.rb +90 -0
  30. data/lib/hanami/providers/assets.rb +44 -0
  31. data/lib/hanami/rake_tasks.rb +19 -18
  32. data/lib/hanami/settings.rb +1 -1
  33. data/lib/hanami/slice.rb +48 -2
  34. data/lib/hanami/slice_configurable.rb +3 -2
  35. data/lib/hanami/version.rb +1 -1
  36. data/lib/hanami/web/rack_logger.rb +1 -1
  37. data/lib/hanami.rb +3 -3
  38. data/spec/integration/action/view_rendering/view_context_spec.rb +221 -0
  39. data/spec/integration/action/view_rendering_spec.rb +0 -18
  40. data/spec/integration/assets/assets_spec.rb +101 -0
  41. data/spec/integration/assets/serve_static_assets_spec.rb +152 -0
  42. data/spec/integration/logging/exception_logging_spec.rb +115 -0
  43. data/spec/integration/logging/notifications_spec.rb +68 -0
  44. data/spec/integration/logging/request_logging_spec.rb +128 -0
  45. data/spec/integration/rack_app/middleware_spec.rb +22 -22
  46. data/spec/integration/rack_app/rack_app_spec.rb +3 -220
  47. data/spec/integration/rake_tasks_spec.rb +107 -0
  48. data/spec/integration/view/config/default_context_spec.rb +149 -0
  49. data/spec/integration/view/{inflector_spec.rb → config/inflector_spec.rb} +1 -1
  50. data/spec/integration/view/config/part_class_spec.rb +147 -0
  51. data/spec/integration/view/config/part_namespace_spec.rb +103 -0
  52. data/spec/integration/view/config/paths_spec.rb +119 -0
  53. data/spec/integration/view/config/scope_class_spec.rb +147 -0
  54. data/spec/integration/view/config/scope_namespace_spec.rb +103 -0
  55. data/spec/integration/view/config/template_spec.rb +38 -0
  56. data/spec/integration/view/context/assets_spec.rb +3 -9
  57. data/spec/integration/view/context/request_spec.rb +3 -7
  58. data/spec/integration/view/helpers/form_helper_spec.rb +174 -0
  59. data/spec/integration/view/helpers/part_helpers_spec.rb +124 -0
  60. data/spec/integration/view/helpers/scope_helpers_spec.rb +84 -0
  61. data/spec/integration/view/helpers/user_defined_helpers/part_helpers_spec.rb +162 -0
  62. data/spec/integration/view/helpers/user_defined_helpers/scope_helpers_spec.rb +119 -0
  63. data/spec/integration/view/slice_configuration_spec.rb +9 -9
  64. data/spec/integration/web/render_detailed_errors_spec.rb +107 -0
  65. data/spec/integration/web/render_errors_spec.rb +242 -0
  66. data/spec/spec_helper.rb +1 -1
  67. data/spec/support/app_integration.rb +46 -2
  68. data/spec/support/matchers.rb +32 -0
  69. data/spec/unit/hanami/config/actions/content_security_policy_spec.rb +24 -36
  70. data/spec/unit/hanami/config/actions/csrf_protection_spec.rb +4 -3
  71. data/spec/unit/hanami/config/actions/default_values_spec.rb +3 -6
  72. data/spec/unit/hanami/config/render_detailed_errors_spec.rb +25 -0
  73. data/spec/unit/hanami/config/render_errors_spec.rb +25 -0
  74. data/spec/unit/hanami/config/views_spec.rb +0 -18
  75. data/spec/unit/hanami/env_spec.rb +11 -25
  76. data/spec/unit/hanami/extensions/view/context_spec.rb +59 -0
  77. data/spec/unit/hanami/helpers/assets_helper/asset_url_spec.rb +109 -0
  78. data/spec/unit/hanami/helpers/assets_helper/audio_tag_spec.rb +132 -0
  79. data/spec/unit/hanami/helpers/assets_helper/favicon_link_tag_spec.rb +91 -0
  80. data/spec/unit/hanami/helpers/assets_helper/image_tag_spec.rb +92 -0
  81. data/spec/unit/hanami/helpers/assets_helper/javascript_tag_spec.rb +143 -0
  82. data/spec/unit/hanami/helpers/assets_helper/stylesheet_link_tag_spec.rb +126 -0
  83. data/spec/unit/hanami/helpers/assets_helper/video_tag_spec.rb +132 -0
  84. data/spec/unit/hanami/helpers/form_helper_spec.rb +2826 -0
  85. data/spec/unit/hanami/router/errors/not_allowed_error_spec.rb +27 -0
  86. data/spec/unit/hanami/router/errors/not_found_error_spec.rb +22 -0
  87. data/spec/unit/hanami/slice_configurable_spec.rb +18 -0
  88. data/spec/unit/hanami/version_spec.rb +1 -1
  89. data/spec/unit/hanami/web/rack_logger_spec.rb +1 -1
  90. metadata +95 -35
  91. data/lib/hanami/assets/app_config.rb +0 -61
  92. data/lib/hanami/assets/config.rb +0 -53
  93. data/spec/integration/action/view_integration_spec.rb +0 -165
  94. data/spec/integration/view/part_namespace_spec.rb +0 -96
  95. data/spec/integration/view/path_spec.rb +0 -56
  96. data/spec/integration/view/template_spec.rb +0 -68
  97. data/spec/isolation/hanami/application/already_configured_spec.rb +0 -19
  98. data/spec/isolation/hanami/application/inherit_anonymous_class_spec.rb +0 -10
  99. data/spec/isolation/hanami/application/inherit_concrete_class_spec.rb +0 -14
  100. data/spec/isolation/hanami/application/not_configured_spec.rb +0 -9
  101. data/spec/isolation/hanami/application/routes/configured_spec.rb +0 -44
  102. data/spec/isolation/hanami/application/routes/not_configured_spec.rb +0 -16
  103. 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