shoulda-matchers 2.8.0 → 3.0.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. checksums.yaml +4 -4
  2. data/.hound_config/ruby.yml +7 -0
  3. data/.travis.yml +11 -54
  4. data/Appraisals +45 -100
  5. data/CONTRIBUTING.md +51 -7
  6. data/Gemfile +7 -19
  7. data/Gemfile.lock +60 -134
  8. data/Guardfile +5 -0
  9. data/NEWS.md +203 -0
  10. data/README.md +95 -50
  11. data/Rakefile +1 -0
  12. data/doc_config/yard/templates/default/layout/html/setup.rb +1 -1
  13. data/gemfiles/4.0.0.gemfile +10 -7
  14. data/gemfiles/4.0.0.gemfile.lock +103 -79
  15. data/gemfiles/4.0.1.gemfile +10 -7
  16. data/gemfiles/4.0.1.gemfile.lock +109 -83
  17. data/gemfiles/4.1.gemfile +10 -7
  18. data/gemfiles/4.1.gemfile.lock +109 -85
  19. data/gemfiles/4.2.gemfile +10 -9
  20. data/gemfiles/4.2.gemfile.lock +86 -78
  21. data/lib/shoulda/matchers.rb +13 -18
  22. data/lib/shoulda/matchers/action_controller.rb +4 -1
  23. data/lib/shoulda/matchers/action_controller/flash_store.rb +95 -0
  24. data/lib/shoulda/matchers/action_controller/{strong_parameters_matcher.rb → permit_matcher.rb} +147 -30
  25. data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +1 -1
  26. data/lib/shoulda/matchers/action_controller/render_template_matcher.rb +1 -1
  27. data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +1 -1
  28. data/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +1 -1
  29. data/lib/shoulda/matchers/action_controller/route_matcher.rb +5 -1
  30. data/lib/shoulda/matchers/action_controller/route_params.rb +15 -6
  31. data/lib/shoulda/matchers/action_controller/session_store.rb +34 -0
  32. data/lib/shoulda/matchers/action_controller/set_flash_matcher.rb +30 -136
  33. data/lib/shoulda/matchers/action_controller/set_session_matcher.rb +28 -109
  34. data/lib/shoulda/matchers/action_controller/set_session_or_flash_matcher.rb +103 -0
  35. data/lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb +1 -12
  36. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +79 -10
  37. data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +10 -0
  38. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +21 -0
  39. data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +24 -0
  40. data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +22 -5
  41. data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +29 -10
  42. data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +27 -10
  43. data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +27 -12
  44. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +56 -20
  45. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +3 -11
  46. data/lib/shoulda/matchers/active_model/validation_message_finder.rb +65 -0
  47. data/lib/shoulda/matchers/active_record/association_matcher.rb +40 -6
  48. data/lib/shoulda/matchers/active_record/association_matchers/join_table_matcher.rb +21 -7
  49. data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +11 -40
  50. data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +1 -1
  51. data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +2 -6
  52. data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +137 -22
  53. data/lib/shoulda/matchers/configuration.rb +20 -0
  54. data/lib/shoulda/matchers/doublespeak.rb +11 -1
  55. data/lib/shoulda/matchers/doublespeak/double.rb +29 -11
  56. data/lib/shoulda/matchers/doublespeak/double_collection.rb +4 -3
  57. data/lib/shoulda/matchers/doublespeak/method_call.rb +35 -0
  58. data/lib/shoulda/matchers/doublespeak/object_double.rb +7 -2
  59. data/lib/shoulda/matchers/doublespeak/proxy_implementation.rb +4 -3
  60. data/lib/shoulda/matchers/doublespeak/stub_implementation.rb +3 -3
  61. data/lib/shoulda/matchers/doublespeak/world.rb +21 -1
  62. data/lib/shoulda/matchers/integrations.rb +43 -0
  63. data/lib/shoulda/matchers/integrations/configuration.rb +68 -0
  64. data/lib/shoulda/matchers/integrations/configuration_error.rb +9 -0
  65. data/lib/shoulda/matchers/integrations/inclusion.rb +20 -0
  66. data/lib/shoulda/matchers/integrations/libraries.rb +15 -0
  67. data/lib/shoulda/matchers/integrations/libraries/action_controller.rb +31 -0
  68. data/lib/shoulda/matchers/integrations/libraries/active_model.rb +26 -0
  69. data/lib/shoulda/matchers/integrations/libraries/active_record.rb +26 -0
  70. data/lib/shoulda/matchers/integrations/libraries/missing_library.rb +19 -0
  71. data/lib/shoulda/matchers/integrations/libraries/rails.rb +30 -0
  72. data/lib/shoulda/matchers/integrations/rails.rb +12 -0
  73. data/lib/shoulda/matchers/integrations/registry.rb +28 -0
  74. data/lib/shoulda/matchers/integrations/test_frameworks.rb +16 -0
  75. data/lib/shoulda/matchers/integrations/test_frameworks/active_support_test_case.rb +37 -0
  76. data/lib/shoulda/matchers/integrations/test_frameworks/minitest_4.rb +36 -0
  77. data/lib/shoulda/matchers/integrations/test_frameworks/minitest_5.rb +37 -0
  78. data/lib/shoulda/matchers/integrations/test_frameworks/missing_test_framework.rb +40 -0
  79. data/lib/shoulda/matchers/integrations/test_frameworks/rspec.rb +29 -0
  80. data/lib/shoulda/matchers/integrations/test_frameworks/test_unit.rb +36 -0
  81. data/lib/shoulda/matchers/rails_shim.rb +0 -40
  82. data/lib/shoulda/matchers/version.rb +1 -1
  83. data/script/SUPPORTED_VERSIONS +1 -1
  84. data/script/update_gems_in_all_appraisals +14 -0
  85. data/shoulda-matchers.gemspec +2 -2
  86. data/spec/acceptance/active_model_integration_spec.rb +4 -1
  87. data/spec/acceptance/independent_matchers_spec.rb +6 -6
  88. data/spec/acceptance/multiple_libraries_integration_spec.rb +52 -0
  89. data/spec/acceptance/rails_integration_spec.rb +15 -5
  90. data/spec/acceptance_spec_helper.rb +8 -0
  91. data/spec/doublespeak_spec_helper.rb +14 -0
  92. data/spec/support/acceptance/adds_shoulda_matchers_to_project.rb +110 -0
  93. data/spec/support/acceptance/helpers.rb +2 -0
  94. data/spec/support/acceptance/helpers/base_helpers.rb +6 -1
  95. data/spec/support/acceptance/helpers/command_helpers.rb +6 -2
  96. data/spec/support/acceptance/helpers/minitest_helpers.rb +0 -8
  97. data/spec/support/acceptance/helpers/n_unit_helpers.rb +25 -0
  98. data/spec/support/acceptance/helpers/rspec_helpers.rb +2 -0
  99. data/spec/support/acceptance/helpers/step_helpers.rb +13 -19
  100. data/spec/support/acceptance/matchers/have_output.rb +1 -1
  101. data/spec/support/tests/bundle.rb +1 -1
  102. data/spec/support/tests/command_runner.rb +25 -13
  103. data/spec/support/tests/current_bundle.rb +47 -0
  104. data/spec/support/tests/database.rb +28 -0
  105. data/spec/support/tests/database_adapters/postgresql.rb +25 -0
  106. data/spec/support/tests/database_adapters/sqlite3.rb +26 -0
  107. data/spec/support/tests/database_configuration.rb +33 -0
  108. data/spec/support/tests/database_configuration_registry.rb +28 -0
  109. data/spec/support/tests/filesystem.rb +25 -2
  110. data/spec/support/unit/helpers/active_record_versions.rb +12 -0
  111. data/spec/support/unit/helpers/class_builder.rb +6 -2
  112. data/spec/support/unit/helpers/column_type_helpers.rb +26 -0
  113. data/spec/support/unit/helpers/controller_builder.rb +0 -28
  114. data/spec/support/unit/helpers/database_helpers.rb +18 -0
  115. data/spec/support/unit/helpers/model_builder.rb +38 -6
  116. data/spec/support/unit/helpers/rails_versions.rb +2 -2
  117. data/spec/support/unit/matchers/fail_with_message_including_matcher.rb +9 -8
  118. data/spec/support/unit/matchers/fail_with_message_matcher.rb +1 -1
  119. data/spec/support/unit/rails_application.rb +29 -13
  120. data/spec/support/unit/record_validating_confirmation_builder.rb +1 -2
  121. data/spec/support/unit/shared_examples/set_session_or_flash.rb +355 -0
  122. data/spec/unit/shoulda/matchers/action_controller/permit_matcher_spec.rb +433 -0
  123. data/spec/unit/shoulda/matchers/action_controller/render_with_layout_matcher_spec.rb +1 -5
  124. data/spec/unit/shoulda/matchers/action_controller/route_matcher_spec.rb +37 -0
  125. data/spec/unit/shoulda/matchers/action_controller/set_flash_matcher_spec.rb +23 -147
  126. data/spec/unit/shoulda/matchers/action_controller/set_session_matcher_spec.rb +8 -285
  127. data/spec/unit/shoulda/matchers/action_controller/set_session_or_flash_matcher_spec.rb +562 -0
  128. data/spec/unit/shoulda/matchers/active_model/allow_value_matcher_spec.rb +81 -14
  129. data/spec/unit/shoulda/matchers/active_model/disallow_value_matcher_spec.rb +16 -8
  130. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/comparison_matcher_spec.rb +101 -9
  131. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/even_number_matcher_spec.rb +39 -1
  132. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher_spec.rb +39 -1
  133. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher_spec.rb +39 -0
  134. data/spec/unit/shoulda/matchers/active_model/validate_exclusion_of_matcher_spec.rb +0 -17
  135. data/spec/unit/shoulda/matchers/active_model/validate_inclusion_of_matcher_spec.rb +0 -17
  136. data/spec/unit/shoulda/matchers/active_model/validate_length_of_matcher_spec.rb +0 -17
  137. data/spec/unit/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +838 -271
  138. data/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +0 -19
  139. data/spec/unit/shoulda/matchers/active_record/association_matcher_spec.rb +93 -0
  140. data/spec/unit/shoulda/matchers/active_record/association_matchers/model_reflection_spec.rb +3 -3
  141. data/spec/unit/shoulda/matchers/active_record/define_enum_for_matcher_spec.rb +25 -0
  142. data/spec/unit/shoulda/matchers/active_record/validate_uniqueness_of_matcher_spec.rb +905 -0
  143. data/spec/unit/shoulda/matchers/doublespeak/double_collection_spec.rb +17 -11
  144. data/spec/unit/shoulda/matchers/doublespeak/double_implementation_registry_spec.rb +1 -1
  145. data/spec/unit/shoulda/matchers/doublespeak/double_spec.rb +144 -43
  146. data/spec/unit/shoulda/matchers/doublespeak/object_double_spec.rb +1 -1
  147. data/spec/unit/shoulda/matchers/doublespeak/proxy_implementation_spec.rb +36 -11
  148. data/spec/unit/shoulda/matchers/doublespeak/stub_implementation_spec.rb +29 -16
  149. data/spec/unit/shoulda/matchers/doublespeak/world_spec.rb +8 -5
  150. data/spec/unit/shoulda/matchers/doublespeak_spec.rb +1 -1
  151. data/spec/unit_spec_helper.rb +15 -14
  152. data/spec/warnings_spy.rb +1 -1
  153. metadata +68 -29
  154. data/docs.watchr +0 -5
  155. data/gemfiles/3.0.gemfile +0 -26
  156. data/gemfiles/3.0.gemfile.lock +0 -173
  157. data/gemfiles/3.1.gemfile +0 -32
  158. data/gemfiles/3.1.gemfile.lock +0 -212
  159. data/gemfiles/3.1_1.9.2.gemfile +0 -32
  160. data/gemfiles/3.1_1.9.2.gemfile.lock +0 -212
  161. data/gemfiles/3.2.gemfile +0 -33
  162. data/gemfiles/3.2.gemfile.lock +0 -212
  163. data/gemfiles/3.2_1.9.2.gemfile +0 -31
  164. data/gemfiles/3.2_1.9.2.gemfile.lock +0 -207
  165. data/lib/shoulda/matchers/assertion_error.rb +0 -27
  166. data/lib/shoulda/matchers/doublespeak/structs.rb +0 -10
  167. data/lib/shoulda/matchers/integrations/nunit_test_case_detection.rb +0 -39
  168. data/lib/shoulda/matchers/integrations/rspec.rb +0 -19
  169. data/lib/shoulda/matchers/integrations/test_unit.rb +0 -34
  170. data/spec/unit/shoulda/matchers/action_controller/strong_parameters_matcher_spec.rb +0 -331
  171. data/spec/unit/shoulda/matchers/active_model/validate_uniqueness_of_matcher_spec.rb +0 -564
@@ -0,0 +1,433 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Shoulda::Matchers::ActionController::PermitMatcher, type: :controller do
4
+ shared_examples 'basic tests' do
5
+ it 'accepts a subset of the permitted attributes' do
6
+ define_controller_with_strong_parameters(action: :create) do |ctrl|
7
+ params_with_conditional_require(ctrl.params).permit(:name, :age)
8
+ end
9
+
10
+ expect(controller).to permit_with_conditional_params(
11
+ permit(:name).for(:create)
12
+ )
13
+ end
14
+
15
+ it 'accepts all of the permitted attributes' do
16
+ define_controller_with_strong_parameters(action: :create) do |ctrl|
17
+ params_with_conditional_require(ctrl.params).permit(:name, :age)
18
+ end
19
+
20
+ expect(controller).to permit_with_conditional_params(
21
+ permit(:name, :age).for(:create)
22
+ )
23
+ end
24
+
25
+ it 'rejects attributes that have not been permitted' do
26
+ define_controller_with_strong_parameters(action: :create) do |ctrl|
27
+ params_with_conditional_require(ctrl.params).permit(:name)
28
+ end
29
+
30
+ expect(controller).not_to permit_with_conditional_params(
31
+ permit(:name, :admin).for(:create)
32
+ )
33
+ end
34
+
35
+ it 'rejects when #permit has not been called' do
36
+ define_controller_with_strong_parameters(action: :create)
37
+
38
+ expect(controller).not_to permit_with_conditional_params(
39
+ permit(:name).for(:create)
40
+ )
41
+ end
42
+
43
+ it 'tracks multiple calls to #permit' do
44
+ sets_of_attributes = [
45
+ [:eta, :diner_id],
46
+ [:phone_number, :address_1, :address_2, :city, :state, :zip]
47
+ ]
48
+
49
+ params = {
50
+ order: { some: 'value' },
51
+ diner: { some: 'value' }
52
+ }
53
+
54
+ define_controller_with_strong_parameters(action: :create) do |ctrl|
55
+ params_with_conditional_require(ctrl.params, :order).
56
+ permit(sets_of_attributes[0])
57
+
58
+ params_with_conditional_require(ctrl.params, :diner).
59
+ permit(sets_of_attributes[1])
60
+ end
61
+
62
+ expect(controller).to permit_with_conditional_params(
63
+ permit(*sets_of_attributes[0]).for(:create),
64
+ params
65
+ )
66
+
67
+ expect(controller).to permit_with_conditional_params(
68
+ permit(*sets_of_attributes[1]).for(:create),
69
+ params
70
+ )
71
+ end
72
+ end
73
+
74
+ it 'requires an action' do
75
+ assertion = -> { expect(controller).to permit(:name) }
76
+
77
+ define_controller_with_strong_parameters
78
+
79
+ expect(&assertion).to raise_error(described_class::ActionNotDefinedError)
80
+ end
81
+
82
+ it 'requires a verb for a non-restful action' do
83
+ define_controller_with_strong_parameters
84
+
85
+ assertion = lambda do
86
+ expect(controller).to permit(:name).for(:authorize)
87
+ end
88
+
89
+ expect(&assertion).to raise_error(described_class::VerbNotDefinedError)
90
+ end
91
+
92
+ context 'when operating on the entire params hash' do
93
+ include_context 'basic tests' do
94
+ def permit_with_conditional_params(permit, _params = {})
95
+ permit
96
+ end
97
+
98
+ def params_with_conditional_require(params, *filters)
99
+ params
100
+ end
101
+ end
102
+ end
103
+
104
+ context 'when operating on a slice of the params hash' do
105
+ include_context 'basic tests' do
106
+ def permit_with_conditional_params(permit, params = nil)
107
+ params ||= { user: { some: 'value' } }
108
+ permit.add_params(params)
109
+ end
110
+
111
+ def params_with_conditional_require(params, *filters)
112
+ if filters.none?
113
+ filters = [:user]
114
+ end
115
+
116
+ params.require(*filters)
117
+ end
118
+ end
119
+
120
+ it 'rejects if asserting that parameters were not permitted, but on the wrong slice' do
121
+ define_controller_with_strong_parameters(action: :create) do
122
+ params.require(:order).permit(:eta, :diner_id)
123
+ end
124
+
125
+ expect(controller).
126
+ not_to permit(:eta, :diner_id).
127
+ for(:create, params: { order: { some: 'value' } }).
128
+ on(:something_else)
129
+ end
130
+ end
131
+
132
+ it 'can be used more than once in the same test' do
133
+ define_controller_with_strong_parameters(action: :create) do
134
+ params.permit(:name)
135
+ end
136
+
137
+ expect(controller).to permit(:name).for(:create)
138
+ expect(controller).not_to permit(:admin).for(:create)
139
+ end
140
+
141
+ it 'allows extra parameters to be passed to the action if it requires them' do
142
+ options = {
143
+ controller_name: 'Posts',
144
+ action: :show,
145
+ routes: -> { get '/posts/:slug', to: 'posts#show' }
146
+ }
147
+
148
+ define_controller_with_strong_parameters(options) do
149
+ params.permit(:name)
150
+ end
151
+
152
+ expect(controller).
153
+ to permit(:name).
154
+ for(:show, verb: :get, params: { slug: 'foo' })
155
+ end
156
+
157
+ it 'works with #update specifically' do
158
+ define_controller_with_strong_parameters(action: :update) do
159
+ params.permit(:name)
160
+ end
161
+
162
+ expect(controller).
163
+ to permit(:name).
164
+ for(:update, params: { id: 1 })
165
+ end
166
+
167
+ describe '#matches?' do
168
+ it 'does not raise an error when #fetch was used instead of #require (issue #495)' do
169
+ matcher = permit(:eta, :diner_id).for(:create)
170
+ matching = -> { matcher.matches?(controller) }
171
+
172
+ define_controller_with_strong_parameters(action: :create) do
173
+ params.fetch(:order, {}).permit(:eta, :diner_id)
174
+ end
175
+
176
+ expect(&matching).not_to raise_error
177
+ end
178
+
179
+ context 'stubbing params on the controller' do
180
+ it 'still allows the original params hash to be modified and accessed prior to the call to #require' do
181
+ actual_user_params = nil
182
+ actual_foo_param = nil
183
+ matcher = permit(:name).for(
184
+ :create,
185
+ params: { user: { some: 'params' } }
186
+ )
187
+
188
+ define_controller_with_strong_parameters(action: :create) do
189
+ params[:foo] = 'bar'
190
+ actual_foo_param = params[:foo]
191
+ actual_user_params = params[:user]
192
+
193
+ params.permit(:name)
194
+ end
195
+
196
+ matcher.matches?(controller)
197
+
198
+ expect(actual_user_params).to eq('some' => 'params')
199
+ expect(actual_foo_param).to eq 'bar'
200
+ end
201
+
202
+ it 'still allows #require to return a slice of the params' do
203
+ expected_user_params = { 'foo' => 'bar' }
204
+ actual_user_params = nil
205
+ matcher = permit(:name).for(
206
+ :update,
207
+ params: { id: 1, user: expected_user_params }
208
+ )
209
+
210
+ define_controller_with_strong_parameters(action: :update) do
211
+ actual_user_params = params.require(:user)
212
+ begin
213
+ actual_user_params.permit(:name)
214
+ rescue
215
+ end
216
+ end
217
+
218
+ matcher.matches?(controller)
219
+
220
+ expect(actual_user_params).to eq expected_user_params
221
+ end
222
+
223
+ it 'does not permanently stub the params hash' do
224
+ matcher = permit(:name).for(:create)
225
+ params_access = -> { controller.params.require(:user) }
226
+
227
+ define_controller_with_strong_parameters(action: :create)
228
+
229
+ matcher.matches?(controller)
230
+
231
+ expect(&params_access).
232
+ to raise_error(::ActionController::ParameterMissing)
233
+ end
234
+
235
+ it 'prevents permanently stubbing params on error' do
236
+ matcher = permit(:name).for(:create)
237
+ params_access = -> { controller.params.require(:user) }
238
+
239
+ define_controller_raising_exception
240
+
241
+ begin
242
+ matcher.matches?(controller)
243
+ rescue simulated_error_class
244
+ end
245
+
246
+ expect(&params_access).
247
+ to raise_error(::ActionController::ParameterMissing)
248
+ end
249
+ end
250
+ end
251
+
252
+ describe '#description' do
253
+ it 'returns the correct string' do
254
+ options = { action: :create, method: :post }
255
+
256
+ define_controller_with_strong_parameters(options) do
257
+ params.permit(:name, :age)
258
+ end
259
+
260
+ matcher = described_class.new([:name, :age, :height]).for(:create)
261
+ expect(matcher.description).to eq(
262
+ '(on POST #create) restrict parameters to :name, :age, and :height'
263
+ )
264
+ end
265
+
266
+ context 'when a verb is specified' do
267
+ it 'returns the correct string' do
268
+ options = { action: :some_action }
269
+
270
+ define_controller_with_strong_parameters(options) do
271
+ params.permit(:name, :age)
272
+ end
273
+
274
+ matcher = described_class.
275
+ new([:name]).
276
+ for(:some_action, verb: :put)
277
+ expect(matcher.description).to eq(
278
+ '(on PUT #some_action) restrict parameters to :name'
279
+ )
280
+ end
281
+ end
282
+ end
283
+
284
+ describe 'positive failure message' do
285
+ it 'includes all missing attributes' do
286
+ define_controller_with_strong_parameters(action: :create) do
287
+ params.permit(:name, :age)
288
+ end
289
+
290
+ assertion = lambda do
291
+ expect(@controller).
292
+ to permit(:name, :age, :city, :country).
293
+ for(:create)
294
+ end
295
+
296
+ message =
297
+ "Expected POST #create to restrict parameters to " +
298
+ ":name, :age, :city, and :country,\n" +
299
+ "but the restricted parameters were :name and :age instead."
300
+
301
+ expect(&assertion).to fail_with_message(message)
302
+ end
303
+ end
304
+
305
+ describe 'negative failure message' do
306
+ it 'includes all attributes that should not have been permitted but were' do
307
+ define_controller_with_strong_parameters(action: :create) do
308
+ params.permit(:name, :age)
309
+ end
310
+
311
+ assertion = lambda do
312
+ expect(controller).not_to permit(:name, :age).for(:create)
313
+ end
314
+
315
+ message =
316
+ "Expected POST #create not to restrict parameters to " +
317
+ ":name and :age,\n" +
318
+ "but it did."
319
+
320
+ expect(&assertion).to fail_with_message(message)
321
+ end
322
+ end
323
+
324
+ describe '#for' do
325
+ context 'when given :create' do
326
+ it 'POSTs to the controller' do
327
+ controller = ActionController::Base.new
328
+ context = build_context
329
+ matcher = permit(:name).for(:create).in_context(context)
330
+
331
+ matcher.matches?(controller)
332
+
333
+ expect(context).to have_received(:post).with(:create, {})
334
+ end
335
+ end
336
+
337
+ context 'when given :update' do
338
+ if rails_gte_4_1?
339
+ it 'PATCHes to the controller' do
340
+ controller = ActionController::Base.new
341
+ context = build_context
342
+ matcher = permit(:name).for(:update).in_context(context)
343
+
344
+ matcher.matches?(controller)
345
+
346
+ expect(context).to have_received(:patch).with(:update, {})
347
+ end
348
+ else
349
+ it 'PUTs to the controller' do
350
+ controller = ActionController::Base.new
351
+ context = build_context
352
+ matcher = permit(:name).for(:update).in_context(context)
353
+
354
+ matcher.matches?(controller)
355
+
356
+ expect(context).to have_received(:put).with(:update, {})
357
+ end
358
+ end
359
+ end
360
+
361
+ context 'when given a custom action and verb' do
362
+ it 'calls the action with the verb' do
363
+ controller = ActionController::Base.new
364
+ context = build_context
365
+ matcher = permit(:name).
366
+ for(:hide, verb: :delete).
367
+ in_context(context)
368
+
369
+ matcher.matches?(controller)
370
+
371
+ expect(context).to have_received(:delete).with(:hide, {})
372
+ end
373
+ end
374
+ end
375
+
376
+ let(:simulated_error_class) do
377
+ Class.new(StandardError)
378
+ end
379
+
380
+ def define_controller_with_strong_parameters(options = {}, &action_body)
381
+ model_name = options.fetch(:model_name, 'User')
382
+ controller_name = options.fetch(:controller_name, 'UsersController')
383
+ collection_name = controller_name.
384
+ to_s.sub(/Controller$/, '').underscore.
385
+ to_sym
386
+ action_name = options.fetch(:action, :some_action)
387
+ routes = options.fetch(:routes, -> { resources collection_name })
388
+
389
+ define_model(model_name)
390
+
391
+ controller_class = define_controller(controller_name) do
392
+ define_method action_name do
393
+ if action_body
394
+ if action_body.arity == 0
395
+ instance_eval(&action_body)
396
+ else
397
+ action_body.call(self)
398
+ end
399
+ end
400
+
401
+ render nothing: true
402
+ end
403
+ end
404
+
405
+ setup_rails_controller_test(controller_class)
406
+
407
+ define_routes(&routes)
408
+
409
+ controller_class
410
+ end
411
+
412
+ def define_controller_raising_exception
413
+ _simulated_error_class = simulated_error_class
414
+
415
+ controller_class = define_controller('Examples') do
416
+ define_method :create do
417
+ raise _simulated_error_class
418
+ end
419
+ end
420
+
421
+ setup_rails_controller_test(controller_class)
422
+
423
+ define_routes do
424
+ get 'examples', to: 'examples#create'
425
+ end
426
+
427
+ controller_class
428
+ end
429
+
430
+ def build_context
431
+ double('context', post: nil, put: nil, patch: nil, delete: nil)
432
+ end
433
+ end
@@ -52,11 +52,7 @@ describe Shoulda::Matchers::ActionController::RenderWithLayoutMatcher, type: :co
52
52
  def set_layout_in_context(context, layout)
53
53
  layouts = Hash.new(0)
54
54
  layouts[layout] = 1
55
- context.instance_variable_set(layouts_ivar, layouts)
56
- end
57
-
58
- def layouts_ivar
59
- Shoulda::Matchers::RailsShim.layouts_ivar
55
+ context.instance_variable_set('@_layouts', layouts)
60
56
  end
61
57
 
62
58
  def controller_without_layout
@@ -48,6 +48,26 @@ describe 'Shoulda::Matchers::ActionController::RouteMatcher', type: :controller
48
48
  to(action: 'show', some: 'other', params: 'here')
49
49
  end
50
50
  end
51
+
52
+ context 'when route has a default format' do
53
+ it 'accepts' do
54
+ expect(controller_with_defined_routes).
55
+ to route(:post, "/#{controller_path}").
56
+ to(action: 'create', format: 'json')
57
+ end
58
+
59
+ it 'accepts when format is specified as a symbol' do
60
+ expect(controller_with_defined_routes).
61
+ to route(:post, "/#{controller_path}").
62
+ to(action: 'create', format: :json)
63
+ end
64
+
65
+ it 'rejects when format is unspecified' do
66
+ expect(controller_with_defined_routes).
67
+ not_to route(:post, "/#{controller_path}").
68
+ to(action: 'create')
69
+ end
70
+ end
51
71
  end
52
72
 
53
73
  context 'when controller and action are specified as a joined string' do
@@ -64,6 +84,20 @@ describe 'Shoulda::Matchers::ActionController::RouteMatcher', type: :controller
64
84
  to("#{controller_path}#show", id: 1)
65
85
  end
66
86
  end
87
+
88
+ context 'when route has the format' do
89
+ it 'accepts' do
90
+ expect(controller_with_defined_routes).
91
+ to route(:post, "/#{controller_path}").
92
+ to("#{controller_path}#create", format: 'json')
93
+ end
94
+
95
+ it 'rejects when format is unspecified' do
96
+ expect(controller_with_defined_routes).
97
+ not_to route(:post, "/#{controller_path}").
98
+ to(action: 'create')
99
+ end
100
+ end
67
101
  end
68
102
 
69
103
  def controller_with_defined_routes
@@ -76,6 +110,9 @@ describe 'Shoulda::Matchers::ActionController::RouteMatcher', type: :controller
76
110
  define_routes do
77
111
  get "/#{_controller_path}", to: "#{_controller_path}#index"
78
112
  get "/#{_controller_path}/:id", to: "#{_controller_path}#show"
113
+ post "/#{_controller_path}",
114
+ to: "#{_controller_path}#create",
115
+ defaults: { format: :json }
79
116
  end
80
117
 
81
118
  controller