mcmire-shoulda-matchers 2.5.0

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 (164) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.travis.yml +32 -0
  4. data/.yardopts +7 -0
  5. data/Appraisals +45 -0
  6. data/CONTRIBUTING.md +41 -0
  7. data/Gemfile +31 -0
  8. data/Gemfile.lock +166 -0
  9. data/MIT-LICENSE +22 -0
  10. data/NEWS.md +299 -0
  11. data/README.md +163 -0
  12. data/Rakefile +116 -0
  13. data/doc_config/gh-pages/index.html.erb +9 -0
  14. data/doc_config/yard/setup.rb +22 -0
  15. data/doc_config/yard/templates/default/fulldoc/html/css/bootstrap.css +5967 -0
  16. data/doc_config/yard/templates/default/fulldoc/html/css/full_list.css +12 -0
  17. data/doc_config/yard/templates/default/fulldoc/html/css/global.css +45 -0
  18. data/doc_config/yard/templates/default/fulldoc/html/css/solarized.css +69 -0
  19. data/doc_config/yard/templates/default/fulldoc/html/css/style.css +283 -0
  20. data/doc_config/yard/templates/default/fulldoc/html/full_list.erb +32 -0
  21. data/doc_config/yard/templates/default/fulldoc/html/full_list_class.erb +1 -0
  22. data/doc_config/yard/templates/default/fulldoc/html/full_list_method.erb +8 -0
  23. data/doc_config/yard/templates/default/fulldoc/html/js/app.js +300 -0
  24. data/doc_config/yard/templates/default/fulldoc/html/js/full_list.js +1 -0
  25. data/doc_config/yard/templates/default/fulldoc/html/js/jquery.stickyheaders.js +289 -0
  26. data/doc_config/yard/templates/default/fulldoc/html/js/underscore.min.js +6 -0
  27. data/doc_config/yard/templates/default/fulldoc/html/setup.rb +8 -0
  28. data/doc_config/yard/templates/default/layout/html/breadcrumb.erb +14 -0
  29. data/doc_config/yard/templates/default/layout/html/fonts.erb +1 -0
  30. data/doc_config/yard/templates/default/layout/html/layout.erb +23 -0
  31. data/doc_config/yard/templates/default/layout/html/search.erb +13 -0
  32. data/doc_config/yard/templates/default/layout/html/setup.rb +8 -0
  33. data/doc_config/yard/templates/default/method_details/html/source.erb +10 -0
  34. data/doc_config/yard/templates/default/module/html/box_info.erb +31 -0
  35. data/features/rails_integration.feature +113 -0
  36. data/features/step_definitions/rails_steps.rb +162 -0
  37. data/features/support/env.rb +5 -0
  38. data/gemfiles/3.0.gemfile +24 -0
  39. data/gemfiles/3.0.gemfile.lock +150 -0
  40. data/gemfiles/3.1.gemfile +27 -0
  41. data/gemfiles/3.1.gemfile.lock +173 -0
  42. data/gemfiles/3.2.gemfile +27 -0
  43. data/gemfiles/3.2.gemfile.lock +171 -0
  44. data/gemfiles/4.0.0.gemfile +28 -0
  45. data/gemfiles/4.0.0.gemfile.lock +172 -0
  46. data/gemfiles/4.0.1.gemfile +28 -0
  47. data/gemfiles/4.0.1.gemfile.lock +172 -0
  48. data/lib/shoulda-matchers.rb +1 -0
  49. data/lib/shoulda/matchers.rb +11 -0
  50. data/lib/shoulda/matchers/action_controller.rb +17 -0
  51. data/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +64 -0
  52. data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +97 -0
  53. data/lib/shoulda/matchers/action_controller/render_template_matcher.rb +81 -0
  54. data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +117 -0
  55. data/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +114 -0
  56. data/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +154 -0
  57. data/lib/shoulda/matchers/action_controller/route_matcher.rb +116 -0
  58. data/lib/shoulda/matchers/action_controller/route_params.rb +48 -0
  59. data/lib/shoulda/matchers/action_controller/set_session_matcher.rb +164 -0
  60. data/lib/shoulda/matchers/action_controller/set_the_flash_matcher.rb +296 -0
  61. data/lib/shoulda/matchers/active_model.rb +30 -0
  62. data/lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb +167 -0
  63. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +314 -0
  64. data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +46 -0
  65. data/lib/shoulda/matchers/active_model/ensure_exclusion_of_matcher.rb +160 -0
  66. data/lib/shoulda/matchers/active_model/ensure_inclusion_of_matcher.rb +417 -0
  67. data/lib/shoulda/matchers/active_model/ensure_length_of_matcher.rb +337 -0
  68. data/lib/shoulda/matchers/active_model/errors.rb +10 -0
  69. data/lib/shoulda/matchers/active_model/exception_message_finder.rb +58 -0
  70. data/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +92 -0
  71. data/lib/shoulda/matchers/active_model/helpers.rb +46 -0
  72. data/lib/shoulda/matchers/active_model/numericality_matchers.rb +9 -0
  73. data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +75 -0
  74. data/lib/shoulda/matchers/active_model/numericality_matchers/even_number_matcher.rb +27 -0
  75. data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +41 -0
  76. data/lib/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher.rb +27 -0
  77. data/lib/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher.rb +26 -0
  78. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +112 -0
  79. data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +77 -0
  80. data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +121 -0
  81. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +380 -0
  82. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +89 -0
  83. data/lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb +372 -0
  84. data/lib/shoulda/matchers/active_model/validation_matcher.rb +97 -0
  85. data/lib/shoulda/matchers/active_model/validation_message_finder.rb +69 -0
  86. data/lib/shoulda/matchers/active_record.rb +22 -0
  87. data/lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb +204 -0
  88. data/lib/shoulda/matchers/active_record/association_matcher.rb +901 -0
  89. data/lib/shoulda/matchers/active_record/association_matchers.rb +9 -0
  90. data/lib/shoulda/matchers/active_record/association_matchers/counter_cache_matcher.rb +41 -0
  91. data/lib/shoulda/matchers/active_record/association_matchers/dependent_matcher.rb +41 -0
  92. data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +81 -0
  93. data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +65 -0
  94. data/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +94 -0
  95. data/lib/shoulda/matchers/active_record/association_matchers/order_matcher.rb +41 -0
  96. data/lib/shoulda/matchers/active_record/association_matchers/source_matcher.rb +41 -0
  97. data/lib/shoulda/matchers/active_record/association_matchers/through_matcher.rb +63 -0
  98. data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +261 -0
  99. data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +149 -0
  100. data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +72 -0
  101. data/lib/shoulda/matchers/active_record/serialize_matcher.rb +181 -0
  102. data/lib/shoulda/matchers/assertion_error.rb +19 -0
  103. data/lib/shoulda/matchers/error.rb +6 -0
  104. data/lib/shoulda/matchers/integrations/rspec.rb +20 -0
  105. data/lib/shoulda/matchers/integrations/test_unit.rb +30 -0
  106. data/lib/shoulda/matchers/rails_shim.rb +50 -0
  107. data/lib/shoulda/matchers/version.rb +6 -0
  108. data/lib/shoulda/matchers/warn.rb +8 -0
  109. data/shoulda-matchers.gemspec +23 -0
  110. data/spec/shoulda/matchers/action_controller/filter_param_matcher_spec.rb +22 -0
  111. data/spec/shoulda/matchers/action_controller/redirect_to_matcher_spec.rb +42 -0
  112. data/spec/shoulda/matchers/action_controller/render_template_matcher_spec.rb +78 -0
  113. data/spec/shoulda/matchers/action_controller/render_with_layout_matcher_spec.rb +63 -0
  114. data/spec/shoulda/matchers/action_controller/rescue_from_matcher_spec.rb +63 -0
  115. data/spec/shoulda/matchers/action_controller/respond_with_matcher_spec.rb +31 -0
  116. data/spec/shoulda/matchers/action_controller/route_matcher_spec.rb +70 -0
  117. data/spec/shoulda/matchers/action_controller/route_params_spec.rb +30 -0
  118. data/spec/shoulda/matchers/action_controller/set_session_matcher_spec.rb +51 -0
  119. data/spec/shoulda/matchers/action_controller/set_the_flash_matcher_spec.rb +153 -0
  120. data/spec/shoulda/matchers/active_model/allow_mass_assignment_of_matcher_spec.rb +111 -0
  121. data/spec/shoulda/matchers/active_model/allow_value_matcher_spec.rb +170 -0
  122. data/spec/shoulda/matchers/active_model/disallow_value_matcher_spec.rb +81 -0
  123. data/spec/shoulda/matchers/active_model/ensure_exclusion_of_matcher_spec.rb +95 -0
  124. data/spec/shoulda/matchers/active_model/ensure_inclusion_of_matcher_spec.rb +320 -0
  125. data/spec/shoulda/matchers/active_model/ensure_length_of_matcher_spec.rb +166 -0
  126. data/spec/shoulda/matchers/active_model/exception_message_finder_spec.rb +111 -0
  127. data/spec/shoulda/matchers/active_model/have_secure_password_matcher_spec.rb +20 -0
  128. data/spec/shoulda/matchers/active_model/helpers_spec.rb +158 -0
  129. data/spec/shoulda/matchers/active_model/numericality_matchers/comparison_matcher_spec.rb +169 -0
  130. data/spec/shoulda/matchers/active_model/numericality_matchers/even_number_matcher_spec.rb +59 -0
  131. data/spec/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher_spec.rb +59 -0
  132. data/spec/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher_spec.rb +57 -0
  133. data/spec/shoulda/matchers/active_model/validate_absence_of_matcher_spec.rb +139 -0
  134. data/spec/shoulda/matchers/active_model/validate_acceptance_of_matcher_spec.rb +41 -0
  135. data/spec/shoulda/matchers/active_model/validate_confirmation_of_matcher_spec.rb +47 -0
  136. data/spec/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +331 -0
  137. data/spec/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +180 -0
  138. data/spec/shoulda/matchers/active_model/validate_uniqueness_of_matcher_spec.rb +398 -0
  139. data/spec/shoulda/matchers/active_model/validation_message_finder_spec.rb +127 -0
  140. data/spec/shoulda/matchers/active_record/accept_nested_attributes_for_matcher_spec.rb +107 -0
  141. data/spec/shoulda/matchers/active_record/association_matcher_spec.rb +860 -0
  142. data/spec/shoulda/matchers/active_record/association_matchers/model_reflection_spec.rb +247 -0
  143. data/spec/shoulda/matchers/active_record/have_db_column_matcher_spec.rb +111 -0
  144. data/spec/shoulda/matchers/active_record/have_db_index_matcher_spec.rb +78 -0
  145. data/spec/shoulda/matchers/active_record/have_readonly_attributes_matcher_spec.rb +41 -0
  146. data/spec/shoulda/matchers/active_record/serialize_matcher_spec.rb +86 -0
  147. data/spec/spec_helper.rb +26 -0
  148. data/spec/support/active_model_versions.rb +13 -0
  149. data/spec/support/active_resource_builder.rb +29 -0
  150. data/spec/support/activemodel_helpers.rb +19 -0
  151. data/spec/support/capture_helpers.rb +19 -0
  152. data/spec/support/class_builder.rb +42 -0
  153. data/spec/support/controller_builder.rb +74 -0
  154. data/spec/support/fail_with_message_including_matcher.rb +33 -0
  155. data/spec/support/fail_with_message_matcher.rb +32 -0
  156. data/spec/support/i18n_faker.rb +10 -0
  157. data/spec/support/mailer_builder.rb +10 -0
  158. data/spec/support/model_builder.rb +81 -0
  159. data/spec/support/rails_versions.rb +18 -0
  160. data/spec/support/shared_examples/numerical_submatcher.rb +19 -0
  161. data/spec/support/shared_examples/numerical_type_submatcher.rb +17 -0
  162. data/spec/support/test_application.rb +120 -0
  163. data/yard.watchr +5 -0
  164. metadata +281 -0
@@ -0,0 +1,337 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveModel
4
+ # The `ensure_length_of` matcher tests usage of the `validates_length_of`
5
+ # matcher. Note that this matcher is intended to be used against string
6
+ # columns and not integer columns.
7
+ #
8
+ # #### Qualifiers
9
+ #
10
+ # One of `is_equal_to`, `is_at_least`, or `is_at_most` must be used.
11
+ #
12
+ # ##### is_at_least
13
+ #
14
+ # Use `is_at_least` to test usage of the `:minimum` option. This asserts
15
+ # that the attribute can take a string which is equal to or longer than
16
+ # the given length and cannot take a string which is shorter.
17
+ #
18
+ # class User
19
+ # include ActiveModel::Model
20
+ #
21
+ # validates_length_of :bio, minimum: 15
22
+ # end
23
+ #
24
+ # # RSpec
25
+ #
26
+ # describe User do
27
+ # it { should ensure_length_of(:bio).is_at_least(15) }
28
+ # end
29
+ #
30
+ # # Test::Unit
31
+ #
32
+ # class UserTest < ActiveSupport::TestCase
33
+ # should ensure_length_of(:bio).is_at_least(15)
34
+ # end
35
+ #
36
+ # ##### is_at_most
37
+ #
38
+ # Use `is_at_most` to test usage of the `:maximum` option. This asserts
39
+ # that the attribute can take a string which is equal to or shorter than
40
+ # the given length and cannot take a string which is longer.
41
+ #
42
+ # class User
43
+ # include ActiveModel::Model
44
+ #
45
+ # validates_length_of :status_update, maximum: 140
46
+ # end
47
+ #
48
+ # # RSpec
49
+ # describe User do
50
+ # it { should ensure_length_of(:status_update).is_at_most(140) }
51
+ # end
52
+ #
53
+ # # Test::Unit
54
+ # class UserTest < ActiveSupport::TestCase
55
+ # should ensure_length_of(:status_update).is_at_most(140)
56
+ # end
57
+ #
58
+ # ##### is_equal_to
59
+ #
60
+ # Use `is_at_equal` to test usage of the `:is` option. This asserts that
61
+ # the attribute can take a string which is exactly equal to the given
62
+ # length and cannot take a string which is shorter or longer.
63
+ #
64
+ # class User
65
+ # include ActiveModel::Model
66
+ #
67
+ # validates_length_of :favorite_superhero, is: 6
68
+ # end
69
+ #
70
+ # # RSpec
71
+ # describe User do
72
+ # it { should ensure_length_of(:favorite_superhero).is_equal_to(6) }
73
+ # end
74
+ #
75
+ # # Test::Unit
76
+ # class UserTest < ActiveSupport::TestCase
77
+ # should ensure_length_of(:favorite_superhero).is_equal_to(6)
78
+ # end
79
+ #
80
+ # ##### is_at_least + is_at_most
81
+ #
82
+ # Use `is_at_least` and `is_at_most` to test usage of the `:in` option.
83
+ #
84
+ # class User
85
+ # include ActiveModel::Model
86
+ #
87
+ # validates_length_of :password, in: 5..30
88
+ # end
89
+ #
90
+ # # RSpec
91
+ # describe User do
92
+ # it { should ensure_length_of(:password).is_at_least(5).is_at_most(30) }
93
+ # end
94
+ #
95
+ # # Test::Unit
96
+ # class UserTest < ActiveSupport::TestCase
97
+ # should ensure_length_of(:password).is_at_least(5).is_at_most(30)
98
+ # end
99
+ #
100
+ # ##### with_message
101
+ #
102
+ # Use `with_message` if you are using a custom validation message.
103
+ #
104
+ # class User
105
+ # include ActiveModel::Model
106
+ #
107
+ # validates_length_of :api_token,
108
+ # minimum: 10,
109
+ # message: "Password isn't long enough"
110
+ # end
111
+ #
112
+ # # RSpec
113
+ # describe User do
114
+ # it do
115
+ # should ensure_length_of(:password).
116
+ # is_at_least(10).
117
+ # with_message("Password isn't long enough")
118
+ # end
119
+ # end
120
+ #
121
+ # # Test::Unit
122
+ # class UserTest < ActiveSupport::TestCase
123
+ # should ensure_length_of(:password).
124
+ # is_at_least(10).
125
+ # with_message("Password isn't long enough")
126
+ # end
127
+ #
128
+ # ##### with_short_message
129
+ #
130
+ # Use `with_short_message` if you are using a custom "too short" message.
131
+ #
132
+ # class User
133
+ # include ActiveModel::Model
134
+ #
135
+ # validates_length_of :secret_key,
136
+ # in: 15..100,
137
+ # too_short: 'Secret key must be more than 15 characters'
138
+ # end
139
+ #
140
+ # # RSpec
141
+ # describe User do
142
+ # it do
143
+ # should ensure_length_of(:secret_key).
144
+ # is_at_least(15).
145
+ # with_short_message('Secret key must be more than 15 characters')
146
+ # end
147
+ # end
148
+ #
149
+ # # Test::Unit
150
+ # class UserTest < ActiveSupport::TestCase
151
+ # should ensure_length_of(:secret_key).
152
+ # is_at_least(15).
153
+ # with_short_message('Secret key must be more than 15 characters')
154
+ # end
155
+ #
156
+ # ##### with_long_message
157
+ #
158
+ # Use `with_long_message` if you are using a custom "too long" message.
159
+ #
160
+ # class User
161
+ # include ActiveModel::Model
162
+ #
163
+ # validates_length_of :secret_key,
164
+ # in: 15..100,
165
+ # too_long: 'Secret key must be less than 100 characters'
166
+ # end
167
+ #
168
+ # # RSpec
169
+ # describe User do
170
+ # it do
171
+ # should ensure_length_of(:secret_key).
172
+ # is_at_most(100).
173
+ # with_long_message('Secret key must be less than 100 characters')
174
+ # end
175
+ # end
176
+ #
177
+ # # Test::Unit
178
+ # class UserTest < ActiveSupport::TestCase
179
+ # should ensure_length_of(:secret_key).
180
+ # is_at_most(100).
181
+ # with_long_message('Secret key must be less than 100 characters')
182
+ # end
183
+ #
184
+ # @return [EnsureLengthOfMatcher]
185
+ #
186
+ def ensure_length_of(attr)
187
+ EnsureLengthOfMatcher.new(attr)
188
+ end
189
+
190
+ # @private
191
+ class EnsureLengthOfMatcher < ValidationMatcher
192
+ include Helpers
193
+
194
+ def initialize(attribute)
195
+ super(attribute)
196
+ @options = {}
197
+ end
198
+
199
+ def is_at_least(length)
200
+ @options[:minimum] = length
201
+ @short_message ||= :too_short
202
+ self
203
+ end
204
+
205
+ def is_at_most(length)
206
+ @options[:maximum] = length
207
+ @long_message ||= :too_long
208
+ self
209
+ end
210
+
211
+ def is_equal_to(length)
212
+ @options[:minimum] = length
213
+ @options[:maximum] = length
214
+ @short_message ||= :wrong_length
215
+ @long_message ||= :wrong_length
216
+ self
217
+ end
218
+
219
+ def with_short_message(message)
220
+ if message
221
+ @short_message = message
222
+ end
223
+ self
224
+ end
225
+
226
+ def with_long_message(message)
227
+ if message
228
+ @long_message = message
229
+ end
230
+ self
231
+ end
232
+
233
+ def with_message(message)
234
+ if message
235
+ @short_message = message
236
+ @long_message = message
237
+ end
238
+ self
239
+ end
240
+
241
+ def description
242
+ description = "ensure #{@attribute} has a length "
243
+ if @options.key?(:minimum) && @options.key?(:maximum)
244
+ if @options[:minimum] == @options[:maximum]
245
+ description << "of exactly #{@options[:minimum]}"
246
+ else
247
+ description << "between #{@options[:minimum]} and #{@options[:maximum]}"
248
+ end
249
+ else
250
+ description << "of at least #{@options[:minimum]}" if @options[:minimum]
251
+ description << "of at most #{@options[:maximum]}" if @options[:maximum]
252
+ end
253
+ description
254
+ end
255
+
256
+ def matches?(subject)
257
+ super(subject)
258
+ translate_messages!
259
+ lower_bound_matches? && upper_bound_matches?
260
+ end
261
+
262
+ private
263
+
264
+ def translate_messages!
265
+ if Symbol === @short_message
266
+ @short_message = default_error_message(@short_message,
267
+ model_name: @subject.class.to_s.underscore,
268
+ instance: @subject,
269
+ attribute: @attribute,
270
+ count: @options[:minimum])
271
+ end
272
+
273
+ if Symbol === @long_message
274
+ @long_message = default_error_message(@long_message,
275
+ model_name: @subject.class.to_s.underscore,
276
+ instance: @subject,
277
+ attribute: @attribute,
278
+ count: @options[:maximum])
279
+ end
280
+ end
281
+
282
+ def lower_bound_matches?
283
+ disallows_lower_length? && allows_minimum_length?
284
+ end
285
+
286
+ def upper_bound_matches?
287
+ disallows_higher_length? && allows_maximum_length?
288
+ end
289
+
290
+ def disallows_lower_length?
291
+ if @options.key?(:minimum)
292
+ @options[:minimum] == 0 ||
293
+ disallows_length_of?(@options[:minimum] - 1, @short_message)
294
+ else
295
+ true
296
+ end
297
+ end
298
+
299
+ def disallows_higher_length?
300
+ if @options.key?(:maximum)
301
+ disallows_length_of?(@options[:maximum] + 1, @long_message)
302
+ else
303
+ true
304
+ end
305
+ end
306
+
307
+ def allows_minimum_length?
308
+ if @options.key?(:minimum)
309
+ allows_length_of?(@options[:minimum], @short_message)
310
+ else
311
+ true
312
+ end
313
+ end
314
+
315
+ def allows_maximum_length?
316
+ if @options.key?(:maximum)
317
+ allows_length_of?(@options[:maximum], @long_message)
318
+ else
319
+ true
320
+ end
321
+ end
322
+
323
+ def allows_length_of?(length, message)
324
+ allows_value_of(string_of_length(length), message)
325
+ end
326
+
327
+ def disallows_length_of?(length, message)
328
+ disallows_value_of(string_of_length(length), message)
329
+ end
330
+
331
+ def string_of_length(length)
332
+ 'x' * length
333
+ end
334
+ end
335
+ end
336
+ end
337
+ end
@@ -0,0 +1,10 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveModel
4
+ # @private
5
+ class CouldNotDetermineValueOutsideOfArray < RuntimeError; end
6
+ # @private
7
+ class NonNullableBooleanError < Shoulda::Matchers::Error; end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,58 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveModel
4
+ # @private
5
+ class ExceptionMessageFinder
6
+ def initialize(instance, attribute, context=nil)
7
+ @instance = instance
8
+ @attribute = attribute
9
+ @context = context
10
+ end
11
+
12
+ def allow_description(allowed_values)
13
+ "doesn't raise when #{@attribute} is set to #{allowed_values}"
14
+ end
15
+
16
+ def messages_description
17
+ if has_messages?
18
+ messages.join
19
+ else
20
+ 'no exception'
21
+ end
22
+ end
23
+
24
+ def has_messages?
25
+ messages.any?
26
+ end
27
+
28
+ def messages
29
+ @messages ||= validate_and_rescue
30
+ end
31
+
32
+ def source_description
33
+ 'exception'
34
+ end
35
+
36
+ def expected_message_from(attribute_message)
37
+ "#{human_attribute_name} #{attribute_message}"
38
+ end
39
+
40
+ private
41
+
42
+ def validate_and_rescue
43
+ @instance.valid?(@context)
44
+ []
45
+ rescue ::ActiveModel::StrictValidationFailed => exception
46
+ [exception.message]
47
+ end
48
+
49
+ def human_attribute_name
50
+ @instance.class.human_attribute_name(@attribute)
51
+ end
52
+ end
53
+
54
+ end
55
+ end
56
+ end
57
+
58
+
@@ -0,0 +1,92 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveModel
4
+ # The `have_secure_password` matcher tests usage of the
5
+ # `has_secure_password` macro.
6
+ #
7
+ # #### Example
8
+ #
9
+ # class User
10
+ # include ActiveModel::Model
11
+ #
12
+ # has_secure_password
13
+ # end
14
+ #
15
+ # # RSpec
16
+ # describe User do
17
+ # it { should have_secure_password }
18
+ # end
19
+ #
20
+ # # Test::Unit
21
+ # class UserTest < ActiveSupport::TestCase
22
+ # should have_secure_password
23
+ # end
24
+ #
25
+ # @return [HaveSecurePasswordMatcher]
26
+ #
27
+ def have_secure_password
28
+ HaveSecurePasswordMatcher.new
29
+ end
30
+
31
+ # @private
32
+ class HaveSecurePasswordMatcher
33
+ attr_reader :failure_message
34
+
35
+ alias failure_message_for_should failure_message
36
+
37
+ CORRECT_PASSWORD = "aBcDe12345"
38
+ INCORRECT_PASSWORD = "password"
39
+
40
+ EXPECTED_METHODS = [
41
+ :authenticate,
42
+ :password=,
43
+ :password_confirmation=,
44
+ :password_digest,
45
+ :password_digest=,
46
+ ]
47
+
48
+ MESSAGES = {
49
+ authenticated_incorrect_password: "expected %{subject} to not authenticate an incorrect password",
50
+ did_not_authenticate_correct_password: "expected %{subject} to authenticate the correct password",
51
+ method_not_found: "expected %{subject} to respond to %{methods}"
52
+ }
53
+
54
+ def description
55
+ "have a secure password"
56
+ end
57
+
58
+ def matches?(subject)
59
+ @subject = subject
60
+
61
+ if failure = validate
62
+ key, params = failure
63
+ @failure_message = MESSAGES[key] % { subject: subject.class }.merge(params)
64
+ end
65
+
66
+ failure.nil?
67
+ end
68
+
69
+ private
70
+
71
+ attr_reader :subject
72
+
73
+ def validate
74
+ missing_methods = EXPECTED_METHODS.select {|m| !subject.respond_to?(m) }
75
+
76
+ if missing_methods.present?
77
+ [:method_not_found, { methods: missing_methods.to_sentence }]
78
+ else
79
+ subject.password = CORRECT_PASSWORD
80
+ subject.password_confirmation = CORRECT_PASSWORD
81
+
82
+ if not subject.authenticate(CORRECT_PASSWORD)
83
+ [:did_not_authenticate_correct_password, {}]
84
+ elsif subject.authenticate(INCORRECT_PASSWORD)
85
+ [:authenticated_incorrect_password, {}]
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end