mcmire-shoulda-matchers 2.5.0 → 2.6.1.docs.1

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/.gitignore +1 -0
  3. data/.travis.yml +9 -0
  4. data/.yardopts +2 -1
  5. data/Appraisals +62 -22
  6. data/Gemfile +1 -1
  7. data/Gemfile.lock +3 -3
  8. data/NEWS.md +87 -4
  9. data/README.md +2 -2
  10. data/Rakefile +18 -0
  11. data/features/activemodel_integration.feature +15 -0
  12. data/features/rails_integration.feature +1 -1
  13. data/features/step_definitions/activemodel_steps.rb +21 -0
  14. data/features/step_definitions/rails_steps.rb +5 -4
  15. data/gemfiles/3.0.gemfile +6 -3
  16. data/gemfiles/3.0.gemfile.lock +14 -4
  17. data/gemfiles/3.1.gemfile +10 -4
  18. data/gemfiles/3.1.gemfile.lock +32 -5
  19. data/gemfiles/3.1_1.9.2.gemfile +21 -0
  20. data/gemfiles/3.1_1.9.2.gemfile.lock +191 -0
  21. data/gemfiles/3.2.gemfile +9 -4
  22. data/gemfiles/3.2.gemfile.lock +28 -5
  23. data/gemfiles/4.0.0.gemfile +11 -3
  24. data/gemfiles/4.0.0.gemfile.lock +42 -5
  25. data/gemfiles/4.0.1.gemfile +11 -3
  26. data/gemfiles/4.0.1.gemfile.lock +42 -5
  27. data/gemfiles/4.1.gemfile +37 -0
  28. data/gemfiles/4.1.gemfile.lock +216 -0
  29. data/lib/shoulda/matchers/action_controller/callback_matcher.rb +202 -0
  30. data/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +1 -1
  31. data/lib/shoulda/matchers/action_controller/set_the_flash_matcher.rb +2 -1
  32. data/lib/shoulda/matchers/action_controller/strong_parameters_matcher.rb +165 -0
  33. data/lib/shoulda/matchers/action_controller.rb +2 -0
  34. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +26 -4
  35. data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +6 -0
  36. data/lib/shoulda/matchers/active_model/ensure_exclusion_of_matcher.rb +2 -0
  37. data/lib/shoulda/matchers/active_model/ensure_inclusion_of_matcher.rb +60 -18
  38. data/lib/shoulda/matchers/active_model/errors.rb +43 -1
  39. data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +14 -3
  40. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +2 -1
  41. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +100 -45
  42. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +38 -5
  43. data/lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb +27 -20
  44. data/lib/shoulda/matchers/active_record/association_matcher.rb +12 -2
  45. data/lib/shoulda/matchers/active_record/association_matchers/inverse_of_matcher.rb +41 -0
  46. data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +24 -1
  47. data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +1 -1
  48. data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +1 -1
  49. data/lib/shoulda/matchers/active_record.rb +1 -0
  50. data/lib/shoulda/matchers/assertion_error.rb +8 -3
  51. data/lib/shoulda/matchers/doublespeak/double.rb +75 -0
  52. data/lib/shoulda/matchers/doublespeak/double_collection.rb +55 -0
  53. data/lib/shoulda/matchers/doublespeak/double_implementation_registry.rb +28 -0
  54. data/lib/shoulda/matchers/doublespeak/object_double.rb +33 -0
  55. data/lib/shoulda/matchers/doublespeak/proxy_implementation.rb +31 -0
  56. data/lib/shoulda/matchers/doublespeak/structs.rb +10 -0
  57. data/lib/shoulda/matchers/doublespeak/stub_implementation.rb +35 -0
  58. data/lib/shoulda/matchers/doublespeak/world.rb +39 -0
  59. data/lib/shoulda/matchers/doublespeak.rb +28 -0
  60. data/lib/shoulda/matchers/error.rb +20 -1
  61. data/lib/shoulda/matchers/independent/delegate_matcher/stubbed_target.rb +35 -0
  62. data/lib/shoulda/matchers/independent/delegate_matcher.rb +293 -0
  63. data/lib/shoulda/matchers/independent.rb +10 -0
  64. data/lib/shoulda/matchers/integrations/nunit_test_case_detection.rb +38 -0
  65. data/lib/shoulda/matchers/integrations/rspec.rb +13 -14
  66. data/lib/shoulda/matchers/integrations/test_unit.rb +19 -15
  67. data/lib/shoulda/matchers/integrations.rb +13 -0
  68. data/lib/shoulda/matchers/rails_shim.rb +16 -0
  69. data/lib/shoulda/matchers/version.rb +1 -1
  70. data/lib/shoulda/matchers.rb +15 -3
  71. data/spec/shoulda/matchers/action_controller/callback_matcher_spec.rb +82 -0
  72. data/spec/shoulda/matchers/action_controller/redirect_to_matcher_spec.rb +2 -2
  73. data/spec/shoulda/matchers/action_controller/render_template_matcher_spec.rb +5 -5
  74. data/spec/shoulda/matchers/action_controller/render_with_layout_matcher_spec.rb +4 -4
  75. data/spec/shoulda/matchers/action_controller/rescue_from_matcher_spec.rb +38 -11
  76. data/spec/shoulda/matchers/action_controller/respond_with_matcher_spec.rb +1 -1
  77. data/spec/shoulda/matchers/action_controller/set_session_matcher_spec.rb +1 -1
  78. data/spec/shoulda/matchers/action_controller/set_the_flash_matcher_spec.rb +6 -6
  79. data/spec/shoulda/matchers/action_controller/strong_parameters_matcher_spec.rb +314 -0
  80. data/spec/shoulda/matchers/active_model/allow_value_matcher_spec.rb +32 -0
  81. data/spec/shoulda/matchers/active_model/ensure_inclusion_of_matcher_spec.rb +553 -211
  82. data/spec/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +22 -0
  83. data/spec/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +38 -0
  84. data/spec/shoulda/matchers/active_model/validate_uniqueness_of_matcher_spec.rb +42 -36
  85. data/spec/shoulda/matchers/active_record/association_matcher_spec.rb +15 -1
  86. data/spec/shoulda/matchers/active_record/association_matchers/model_reflection_spec.rb +4 -0
  87. data/spec/shoulda/matchers/active_record/have_db_index_matcher_spec.rb +4 -0
  88. data/spec/shoulda/matchers/doublespeak/double_collection_spec.rb +102 -0
  89. data/spec/shoulda/matchers/doublespeak/double_implementation_registry_spec.rb +21 -0
  90. data/spec/shoulda/matchers/doublespeak/double_spec.rb +144 -0
  91. data/spec/shoulda/matchers/doublespeak/object_double_spec.rb +77 -0
  92. data/spec/shoulda/matchers/doublespeak/proxy_implementation_spec.rb +40 -0
  93. data/spec/shoulda/matchers/doublespeak/stub_implementation_spec.rb +88 -0
  94. data/spec/shoulda/matchers/doublespeak/world_spec.rb +88 -0
  95. data/spec/shoulda/matchers/doublespeak_spec.rb +19 -0
  96. data/spec/shoulda/matchers/independent/delegate_matcher/stubbed_target_spec.rb +43 -0
  97. data/spec/shoulda/matchers/independent/delegate_matcher_spec.rb +250 -0
  98. data/spec/spec_helper.rb +15 -0
  99. data/spec/support/activemodel_helpers.rb +6 -2
  100. data/spec/support/controller_builder.rb +29 -1
  101. data/spec/support/rails_versions.rb +4 -0
  102. data/spec/support/test_application.rb +1 -1
  103. metadata +59 -10
@@ -0,0 +1,314 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shoulda::Matchers::ActionController do
4
+ describe '#permit' do
5
+ it 'matches when the sent parameter is allowed' do
6
+ controller_for_resource_with_strong_parameters(action: :create) do
7
+ params.require(:user).permit(:name)
8
+ end
9
+
10
+ expect(@controller).to permit(:name).for(:create)
11
+ end
12
+
13
+ it 'does not match when the sent parameter is not allowed' do
14
+ controller_for_resource_with_strong_parameters(action: :create) do
15
+ params.require(:user).permit(:name)
16
+ end
17
+
18
+ expect(@controller).not_to permit(:admin).for(:create)
19
+ end
20
+
21
+ it 'matches against multiple attributes' do
22
+ controller_for_resource_with_strong_parameters(action: :create) do
23
+ params.require(:user).permit(:name, :age)
24
+ end
25
+
26
+ expect(@controller).to permit(:name, :age).for(:create)
27
+ end
28
+ end
29
+ end
30
+
31
+ describe Shoulda::Matchers::ActionController::StrongParametersMatcher do
32
+ describe '#description' do
33
+ it 'returns the correct string' do
34
+ options = { action: :create, method: :post }
35
+ controller_for_resource_with_strong_parameters(options) do
36
+ params.permit(:name, :age)
37
+ end
38
+
39
+ matcher = described_class.new([:name, :age, :height]).for(:create)
40
+ expect(matcher.description).
41
+ to eq 'permit POST #create to receive parameters :name, :age, and :height'
42
+ end
43
+
44
+ context 'when a verb is specified' do
45
+ it 'returns the correct string' do
46
+ options = { action: :some_action }
47
+ controller_for_resource_with_strong_parameters(options) do
48
+ params.permit(:name, :age)
49
+ end
50
+
51
+ matcher = described_class.new([:name]).
52
+ for(:some_action, verb: :put)
53
+ expect(matcher.description).
54
+ to eq 'permit PUT #some_action to receive parameters :name'
55
+ end
56
+ end
57
+ end
58
+
59
+ describe '#matches?' do
60
+ it 'is true for a subset of the allowable attributes' do
61
+ controller_for_resource_with_strong_parameters(action: :create) do
62
+ params.require(:user).permit(:name)
63
+ end
64
+
65
+ matcher = described_class.new([:name]).in_context(self).for(:create)
66
+ expect(matcher.matches?(@controller)).to be_true
67
+ end
68
+
69
+ it 'is true for all the allowable attributes' do
70
+ controller_for_resource_with_strong_parameters(action: :create) do
71
+ params.require(:user).permit(:name, :age)
72
+ end
73
+
74
+ matcher = described_class.new([:name, :age]).in_context(self).for(:create)
75
+ expect(matcher.matches?(@controller)).to be_true
76
+ end
77
+
78
+ it 'is false when any attributes are not allowed' do
79
+ controller_for_resource_with_strong_parameters(action: :create) do
80
+ params.require(:user).permit(:name)
81
+ end
82
+
83
+ matcher = described_class.new([:name, :admin]).in_context(self).for(:create)
84
+ expect(matcher.matches?(@controller)).to be_false
85
+ end
86
+
87
+ it 'is false when permit is not called' do
88
+ controller_for_resource_with_strong_parameters(action: :create)
89
+
90
+ matcher = described_class.new([:name]).in_context(self).for(:create)
91
+ expect(matcher.matches?(@controller)).to be_false
92
+ end
93
+
94
+ it 'requires an action' do
95
+ controller_for_resource_with_strong_parameters
96
+ matcher = described_class.new([:name])
97
+ expect { matcher.matches?(@controller) }.
98
+ to raise_error(described_class::ActionNotDefinedError)
99
+ end
100
+
101
+ it 'requires a verb for non-restful action' do
102
+ controller_for_resource_with_strong_parameters
103
+ matcher = described_class.new([:name]).for(:authorize)
104
+ expect { matcher.matches?(@controller) }.
105
+ to raise_error(described_class::VerbNotDefinedError)
106
+ end
107
+
108
+ it 'works with routes that require extra params' do
109
+ options = {
110
+ controller_name: 'Posts',
111
+ action: :show,
112
+ routes: -> {
113
+ get '/posts/:slug', to: 'posts#show'
114
+ }
115
+ }
116
+ controller_for_resource_with_strong_parameters(options) do
117
+ params.require(:user).permit(:name)
118
+ end
119
+
120
+ matcher = described_class.new([:name]).
121
+ in_context(self).
122
+ for(:show, verb: :get, params: { slug: 'foo' })
123
+ expect(matcher.matches?(@controller)).to be_true
124
+ end
125
+
126
+ it 'works with #update specifically' do
127
+ controller_for_resource_with_strong_parameters(action: :update) do
128
+ params.require(:user).permit(:name)
129
+ end
130
+
131
+ matcher = described_class.new([:name]).
132
+ in_context(self).
133
+ for(:update, params: { id: 1 })
134
+ expect(matcher.matches?(@controller)).to be_true
135
+ end
136
+
137
+ it 'does not raise an error when #fetch was used instead of #require (issue #495)' do
138
+ controller_for_resource_with_strong_parameters(action: :create) do
139
+ params.fetch(:order, {}).permit(:eta, :diner_id)
140
+ end
141
+
142
+ matcher = described_class.new([:eta, :diner_id]).
143
+ in_context(self).
144
+ for(:create)
145
+ expect(matcher.matches?(@controller)).to be_true
146
+ end
147
+
148
+ it 'tracks multiple calls to #permit' do
149
+ sets_of_attributes = [
150
+ [:eta, :diner_id],
151
+ [:phone_number, :address_1, :address_2, :city, :state, :zip]
152
+ ]
153
+ controller_for_resource_with_strong_parameters(action: :create) do
154
+ params.require(:order).permit(sets_of_attributes[0])
155
+ params.require(:diner).permit(sets_of_attributes[1])
156
+ end
157
+
158
+ matcher = described_class.new(sets_of_attributes[0]).
159
+ in_context(self).
160
+ for(:create)
161
+ expect(matcher.matches?(@controller)).to be_true
162
+
163
+ matcher = described_class.new(sets_of_attributes[1]).
164
+ in_context(self).
165
+ for(:create)
166
+ expect(matcher.matches?(@controller)).to be_true
167
+ end
168
+
169
+ context 'stubbing params on the controller' do
170
+ it 'still allows the original params to be set and accessed' do
171
+ actual_user_params = nil
172
+ actual_foo_param = nil
173
+
174
+ controller_for_resource_with_strong_parameters(action: :create) do
175
+ params[:foo] = 'bar'
176
+ actual_foo_param = params[:foo]
177
+
178
+ actual_user_params = params[:user]
179
+
180
+ params.require(:user).permit(:name)
181
+ end
182
+
183
+ matcher = described_class.new([:name]).
184
+ in_context(self).
185
+ for(:create, params: { user: { some: 'params' } })
186
+ matcher.matches?(@controller)
187
+
188
+ expect(actual_user_params).to eq('some' => 'params')
189
+ expect(actual_foo_param).to eq 'bar'
190
+ end
191
+
192
+ it 'stubs the params during the controller action' do
193
+ controller_for_resource_with_strong_parameters(action: :create) do
194
+ params.require(:user)
195
+ end
196
+
197
+ matcher = described_class.new([:name]).in_context(self).for(:create)
198
+
199
+ expect { matcher.matches?(@controller) }.not_to raise_error
200
+ end
201
+
202
+ it 'does not permanently stub params' do
203
+ controller_for_resource_with_strong_parameters(action: :create)
204
+
205
+ matcher = described_class.new([:name]).in_context(self).for(:create)
206
+ matcher.matches?(@controller)
207
+
208
+ expect {
209
+ @controller.params.require(:user)
210
+ }.to raise_error(::ActionController::ParameterMissing)
211
+ end
212
+
213
+ it 'prevents permanently stubbing params on error' do
214
+ stub_controller_with_exception
215
+
216
+ begin
217
+ matcher = described_class.new([:name]).in_context(self).for(:create)
218
+ matcher.matches?(@controller)
219
+ rescue SimulatedError
220
+ end
221
+
222
+ expect {
223
+ @controller.params.require(:user)
224
+ }.to raise_error(::ActionController::ParameterMissing)
225
+ end
226
+ end
227
+ end
228
+
229
+ describe 'failure message' do
230
+ it 'includes all missing attributes' do
231
+ controller_for_resource_with_strong_parameters(action: :create) do
232
+ params.require(:user).permit(:name, :age)
233
+ end
234
+
235
+ expect {
236
+ expect(@controller).to permit(:name, :age, :city, :country).for(:create)
237
+ }.to fail_with_message('Expected controller to permit city and country, but it did not.')
238
+ end
239
+
240
+ it 'includes all attributes that should not have been allowed but were' do
241
+ controller_for_resource_with_strong_parameters(action: :create) do
242
+ params.require(:user).permit(:name, :age)
243
+ end
244
+
245
+ expect {
246
+ expect(@controller).not_to permit(:name, :age).for(:create)
247
+ }.to fail_with_message('Expected controller not to permit name and age, but it did.')
248
+ end
249
+ end
250
+
251
+ describe '#for' do
252
+ context 'when given :create' do
253
+ it 'POSTs to the controller' do
254
+ controller = ActionController::Base.new
255
+ context = mock()
256
+ context.expects(:post).with(:create, {})
257
+ matcher = described_class.new([:name]).in_context(context).for(:create)
258
+
259
+ matcher.matches?(controller)
260
+ end
261
+ end
262
+
263
+ context 'when given :update' do
264
+ if rails_gte_4_1?
265
+ it 'PATCHes to the controller' do
266
+ controller = ActionController::Base.new
267
+ context = mock()
268
+ context.expects(:patch).with(:update, {})
269
+ matcher = described_class.new([:name]).in_context(context).for(:update)
270
+
271
+ matcher.matches?(controller)
272
+ end
273
+ else
274
+ it 'PUTs to the controller' do
275
+ controller = ActionController::Base.new
276
+ context = mock()
277
+ context.expects(:put).with(:update, {})
278
+ matcher = described_class.new([:name]).in_context(context).for(:update)
279
+
280
+ matcher.matches?(controller)
281
+ end
282
+ end
283
+ end
284
+
285
+ context 'when given a custom action and verb' do
286
+ it 'calls the action with the verb' do
287
+ controller = ActionController::Base.new
288
+ context = mock()
289
+ context.expects(:delete).with(:hide, {})
290
+ matcher = described_class.new([:name]).
291
+ in_context(context).
292
+ for(:hide, verb: :delete)
293
+
294
+ matcher.matches?(controller)
295
+ end
296
+ end
297
+ end
298
+
299
+ def stub_controller_with_exception
300
+ controller_class = define_controller('Examples') do
301
+ def create
302
+ raise SimulatedError
303
+ end
304
+ end
305
+
306
+ setup_rails_controller_test(controller_class)
307
+
308
+ define_routes do
309
+ get 'examples', to: 'examples#create'
310
+ end
311
+ end
312
+
313
+ class SimulatedError < StandardError; end
314
+ end
@@ -24,6 +24,20 @@ describe Shoulda::Matchers::ActiveModel::AllowValueMatcher do
24
24
  end
25
25
  end
26
26
 
27
+ describe '#_after_setting_value' do
28
+ it 'sets a block which is yielded after each value is set on the attribute' do
29
+ attribute = :attr
30
+ record = define_model(:example, attribute => :string).new
31
+ matcher = described_class.new('a', 'b', 'c').for(attribute)
32
+ call_count = 0
33
+
34
+ matcher._after_setting_value { call_count += 1 }
35
+ matcher.matches?(record)
36
+
37
+ expect(call_count).to eq 3
38
+ end
39
+ end
40
+
27
41
  context 'an attribute with a validation' do
28
42
  it 'allows a good value' do
29
43
  expect(validating_format(with: /abc/)).to allow_value('abcde').for(:attr)
@@ -54,6 +68,24 @@ describe Shoulda::Matchers::ActiveModel::AllowValueMatcher do
54
68
  expect(validating_format(with: /abc/, message: 'bad value')).
55
69
  not_to allow_value('xyz').for(:attr).with_message(/bad/)
56
70
  end
71
+
72
+ it 'allows interpolation values for the message to be provided' do
73
+ options = {
74
+ attribute_name: :attr,
75
+ attribute_type: :string
76
+ }
77
+
78
+ record = record_with_custom_validation(options) do
79
+ if self.attr == 'xyz'
80
+ self.errors.add :attr, :greater_than, count: 2
81
+ end
82
+ end
83
+
84
+ expect(record).
85
+ not_to allow_value('xyz').
86
+ for(:attr).
87
+ with_message(:greater_than, values: { count: 2 })
88
+ end
57
89
  end
58
90
 
59
91
  context 'an attribute where the message occurs on another attribute' do