shoulda 3.4.0 → 4.0.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 (50) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -7
  3. data/.hound.yml +3 -0
  4. data/.rubocop.yml +192 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +33 -2
  7. data/Appraisals +109 -8
  8. data/CHANGELOG.md +10 -0
  9. data/CONTRIBUTING.md +6 -1
  10. data/Gemfile +13 -0
  11. data/README.md +67 -74
  12. data/Rakefile +24 -10
  13. data/gemfiles/rails_4_2.gemfile +34 -0
  14. data/gemfiles/rails_4_2.gemfile.lock +240 -0
  15. data/gemfiles/rails_5_0.gemfile +32 -0
  16. data/gemfiles/rails_5_0.gemfile.lock +232 -0
  17. data/gemfiles/rails_5_1.gemfile +33 -0
  18. data/gemfiles/rails_5_1.gemfile.lock +247 -0
  19. data/gemfiles/rails_5_2.gemfile +35 -0
  20. data/gemfiles/rails_5_2.gemfile.lock +266 -0
  21. data/gemfiles/rails_6_0.gemfile +37 -0
  22. data/gemfiles/rails_6_0.gemfile.lock +291 -0
  23. data/lib/shoulda/version.rb +1 -1
  24. data/script/install_gems_in_all_appraisals +16 -0
  25. data/script/run_all_tests +16 -0
  26. data/script/supported_ruby_versions +7 -0
  27. data/script/update_gem_in_all_appraisals +17 -0
  28. data/script/update_gems_in_all_appraisals +16 -0
  29. data/shoulda.gemspec +23 -23
  30. data/test/acceptance/integrates_with_rails_test.rb +580 -0
  31. data/test/acceptance_test_helper.rb +43 -0
  32. data/test/support/acceptance/add_shoulda_to_project.rb +73 -0
  33. data/test/support/acceptance/helpers/array_helpers.rb +13 -0
  34. data/test/support/acceptance/helpers/pluralization_helpers.rb +13 -0
  35. data/test/support/acceptance/matchers/have_output.rb +33 -0
  36. data/test/support/acceptance/matchers/indicate_that_tests_were_run.rb +109 -0
  37. data/test/support/acceptance/rails_application_with_shoulda.rb +47 -0
  38. data/test/support/current_bundle.rb +61 -0
  39. data/test/support/snowglobe.rb +5 -0
  40. data/test/test_helper.rb +23 -0
  41. metadata +61 -153
  42. data/features/rails_integration.feature +0 -87
  43. data/features/step_definitions/rails_steps.rb +0 -77
  44. data/features/support/env.rb +0 -14
  45. data/gemfiles/3.0.gemfile +0 -7
  46. data/gemfiles/3.0.gemfile.lock +0 -127
  47. data/gemfiles/3.1.gemfile +0 -9
  48. data/gemfiles/3.1.gemfile.lock +0 -149
  49. data/gemfiles/3.2.gemfile +0 -9
  50. data/gemfiles/3.2.gemfile.lock +0 -146
@@ -0,0 +1,580 @@
1
+ require 'acceptance_test_helper'
2
+
3
+ class ShouldaIntegratesWithRailsTest < AcceptanceTest
4
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
5
+ def setup
6
+ app.create
7
+
8
+ app.write_file 'db/migrate/1_create_users.rb', <<-FILE
9
+ class CreateUsers < #{app.migration_class_name}
10
+ def self.up
11
+ create_table :categories_users do |t|
12
+ t.integer :category_id
13
+ t.integer :user_id
14
+ end
15
+
16
+ create_table :categories do |t|
17
+ end
18
+
19
+ create_table :cities do |t|
20
+ end
21
+
22
+ create_table :lives do |t|
23
+ t.integer :user_id
24
+ end
25
+
26
+ create_table :issues do |t|
27
+ t.integer :user_id
28
+ end
29
+
30
+ create_table :users do |t|
31
+ t.integer :account_id
32
+ t.integer :city_id
33
+ t.string :email
34
+ t.integer :age
35
+ t.integer :status
36
+ t.string :aspects
37
+ end
38
+
39
+ add_index :users, :account_id
40
+ end
41
+ end
42
+ FILE
43
+
44
+ app.run_migrations!
45
+
46
+ app.write_file 'app/models/category.rb', <<-FILE
47
+ class Category < ActiveRecord::Base
48
+ end
49
+ FILE
50
+
51
+ app.write_file 'app/models/city.rb', <<-FILE
52
+ class City < ActiveRecord::Base
53
+ end
54
+ FILE
55
+
56
+ app.write_file 'app/models/issue.rb', <<-FILE
57
+ class Issue < ActiveRecord::Base
58
+ end
59
+ FILE
60
+
61
+ app.write_file 'app/models/life.rb', <<-FILE
62
+ class Life < ActiveRecord::Base
63
+ end
64
+ FILE
65
+
66
+ app.write_file 'app/models/person.rb', <<-FILE
67
+ class Person
68
+ # Note: All of these validations are listed in the same order as what's
69
+ # defined in the test (see below)
70
+
71
+ include ActiveModel::Model
72
+ include ActiveModel::SecurePassword
73
+
74
+ attr_accessor(
75
+ :age,
76
+ :card_number,
77
+ :email,
78
+ :foods,
79
+ :nothing,
80
+ :password_digest,
81
+ :some_other_attribute,
82
+ :some_other_attribute_confirmation,
83
+ :something,
84
+ :terms_of_service,
85
+ :workouts,
86
+ :some_other_attribute,
87
+ )
88
+
89
+ validate :email_looks_like_an_email
90
+
91
+ delegate :a_method, to: :some_delegate_object
92
+
93
+ has_secure_password
94
+
95
+ validates_absence_of :nothing
96
+ validates_acceptance_of :terms_of_service
97
+ validates_confirmation_of :password
98
+ validates_exclusion_of :workouts, in: ["biceps"]
99
+ validates_inclusion_of :foods, in: ["spaghetti"]
100
+ validates_length_of :card_number, maximum: 16
101
+ validates_numericality_of :age
102
+ validates_presence_of :something
103
+
104
+ def some_delegate_object
105
+ Object.new.instance_eval do
106
+ def a_method; end
107
+ end
108
+ end
109
+
110
+ private
111
+
112
+ def email_looks_like_an_email
113
+ if email !~ /@/
114
+ errors.add :email, "invalid"
115
+ end
116
+ end
117
+ end
118
+ FILE
119
+
120
+ app.write_file 'app/models/user.rb', <<-FILE
121
+ class User < ActiveRecord::Base
122
+ # Note: All of these validations are listed in the same order as what's
123
+ # defined in the test (see below)
124
+
125
+ belongs_to :city
126
+ enum status: { inactive: 0, active: 1 }
127
+ attr_readonly :username
128
+ has_and_belongs_to_many :categories
129
+ has_many :issues
130
+ has_one :life
131
+ serialize :aspects
132
+ validates_uniqueness_of :email
133
+ accepts_nested_attributes_for :issues
134
+ end
135
+ FILE
136
+
137
+ app.write_file 'app/controllers/examples_controller.rb', <<-FILE
138
+ class ExamplesController < ApplicationController
139
+ # Note: All of these validations are listed in the same order as what's
140
+ # defined in the test (see below)
141
+
142
+ before_action :some_before_action
143
+ after_action :some_after_action
144
+ around_action :some_around_action
145
+
146
+ rescue_from ActiveRecord::RecordNotFound, with: :handle_not_found
147
+
148
+ layout "application"
149
+
150
+ def index
151
+ render :index
152
+ head :ok
153
+ end
154
+
155
+ def create
156
+ create_params
157
+ flash[:success] = "Example created"
158
+ session[:some_key] = "some value"
159
+ redirect_to action: :index
160
+ end
161
+
162
+ def handle_not_found
163
+ end
164
+
165
+ private
166
+
167
+ def some_before_action
168
+ end
169
+
170
+ def some_after_action
171
+ end
172
+
173
+ def some_around_action
174
+ yield
175
+ end
176
+
177
+ def create_params
178
+ params.require(:user).permit(:email, :password)
179
+ end
180
+ end
181
+ FILE
182
+
183
+ app.write_file 'app/views/examples/index.html.erb', <<-FILE
184
+ Some content here
185
+ FILE
186
+
187
+ app.write_file 'config/routes.rb', <<-FILE
188
+ Rails.application.routes.draw do
189
+ resources :examples, only: [:index, :create]
190
+ end
191
+ FILE
192
+ end
193
+
194
+ def test_succeeding_assertions_for_active_model
195
+ app.write_file 'test/models/person_test.rb', <<-FILE
196
+ require 'test_helper'
197
+
198
+ class PersonTest < ActiveSupport::TestCase
199
+ # Note: All of these matchers are listed in alphabetical order so we can
200
+ # compare with what is listed inside of the shoulda-matchers README
201
+
202
+ should allow_value("john@smith.com").for(:email)
203
+ should_not allow_value("john").for(:email)
204
+
205
+ should delegate_method(:a_method).to(:some_delegate_object)
206
+ should_not delegate_method(:some_other_method).to(:some_other_object)
207
+
208
+ should have_secure_password
209
+
210
+ should validate_absence_of(:nothing)
211
+ should_not validate_absence_of(:some_other_attribute)
212
+
213
+ should validate_acceptance_of(:terms_of_service)
214
+ should_not validate_acceptance_of(:some_other_attribute)
215
+
216
+ should validate_confirmation_of(:password)
217
+ should_not validate_confirmation_of(:some_other_attribute)
218
+
219
+ should validate_exclusion_of(:workouts).in_array(["biceps"])
220
+ should_not validate_exclusion_of(:some_other_attribute).
221
+ in_array(["whatever"])
222
+
223
+ should validate_inclusion_of(:foods).in_array(["spaghetti"])
224
+ should_not validate_inclusion_of(:some_other_attribute).
225
+ in_array(["whatever"])
226
+
227
+ should validate_length_of(:card_number).is_at_most(16)
228
+ should_not validate_length_of(:some_other_attribute).is_at_most(16)
229
+
230
+ should validate_numericality_of(:age)
231
+ should_not validate_numericality_of(:some_other_attribute)
232
+
233
+ should validate_presence_of(:something)
234
+ should_not validate_presence_of(:some_other_attribute)
235
+ end
236
+ FILE
237
+
238
+ result = app.run_n_unit_test_suite
239
+
240
+ assert_accepts indicate_that_tests_were_run(failures: 0), result
241
+ end
242
+
243
+ def test_succeeding_assertions_for_active_record
244
+ app.write_file 'test/models/user_test.rb', <<-FILE
245
+ require 'test_helper'
246
+
247
+ class UserTest < ActiveSupport::TestCase
248
+ # Note: All of these matchers are listed in alphabetical order so we can
249
+ # compare with what is listed inside of the shoulda-matchers README
250
+
251
+ should belong_to(:city)
252
+ should_not belong_to(:some_other_attribute)
253
+
254
+ should define_enum_for(:status).with_values(inactive: 0, active: 1)
255
+ should_not define_enum_for(:status).with_values(foo: "bar")
256
+
257
+ should have_db_column(:age)
258
+ should_not have_db_column(:some_other_attribute)
259
+
260
+ should have_db_index(:account_id)
261
+ should_not have_db_index(:some_other_attribute)
262
+
263
+ should have_readonly_attribute(:username)
264
+ should_not have_readonly_attribute(:some_other_attribute)
265
+
266
+ should have_and_belong_to_many(:categories)
267
+ should_not have_and_belong_to_many(:whatevers)
268
+
269
+ should have_many(:issues)
270
+ should_not have_many(:whatevers)
271
+
272
+ should have_one(:life)
273
+ should_not have_one(:whatever)
274
+
275
+ should serialize(:aspects)
276
+ should_not serialize(:age)
277
+
278
+ should validate_uniqueness_of(:email)
279
+ should_not validate_uniqueness_of(:some_other_attribute)
280
+
281
+ should accept_nested_attributes_for(:issues)
282
+ should_not accept_nested_attributes_for(:some_other_attribute)
283
+ end
284
+ FILE
285
+
286
+ result = app.run_n_unit_test_suite
287
+
288
+ assert_accepts indicate_that_tests_were_run(failures: 0), result
289
+ end
290
+
291
+ def test_succeeding_assertions_for_action_controller
292
+ app.write_file 'test/controllers/examples_controller_test.rb', <<-FILE
293
+ require 'test_helper'
294
+
295
+ class ExamplesControllerTest < ActionController::TestCase
296
+ context "GET #index" do
297
+ setup do
298
+ get :index
299
+ end
300
+
301
+ # Note: All of these matchers are listed in alphabetical order so we
302
+ # can compare with what is listed inside of the shoulda-matchers
303
+ # README
304
+
305
+ should use_before_action(:some_before_action)
306
+ should_not use_before_action(:some_other_before_action)
307
+ should use_after_action(:some_after_action)
308
+ should_not use_after_action(:some_other_after_action)
309
+ should use_around_action(:some_around_action)
310
+ should_not use_around_action(:some_other_around_action)
311
+
312
+ # This is one of the defaults for Rails
313
+ should filter_param(:password)
314
+ should_not filter_param(:some_other_param)
315
+
316
+ should rescue_from(ActiveRecord::RecordNotFound).
317
+ with(:handle_not_found)
318
+ should_not rescue_from(ActiveRecord::RecordNotFound).
319
+ with(:some_other_method)
320
+
321
+ should render_template(:index)
322
+ should_not render_template(:some_other_action)
323
+
324
+ should render_with_layout("application")
325
+ should_not render_with_layout("some_other_layout")
326
+
327
+ should respond_with(:ok)
328
+ should_not respond_with(:some_other_status)
329
+
330
+ should route(:get, "/examples").to(action: :index)
331
+ should_not route(:get, "/examples").to(action: :something_else)
332
+ end
333
+
334
+ context "POST #create" do
335
+ setup do
336
+ if ActionPack::VERSION::STRING.start_with?("4.")
337
+ post :create, {
338
+ user: {
339
+ email: "some@email.com",
340
+ password: "somepassword"
341
+ }
342
+ }
343
+ else
344
+ post :create, params: {
345
+ user: {
346
+ email: "some@email.com",
347
+ password: "somepassword"
348
+ }
349
+ }
350
+ end
351
+ end
352
+
353
+ should permit(:email, :password).
354
+ for(:create, params: {
355
+ user: {
356
+ email: "some@email.com",
357
+ password: "somepassword"
358
+ }
359
+ }).
360
+ on(:user)
361
+ should_not permit(:foo, :bar).
362
+ for(:create, params: {
363
+ user: {
364
+ email: "some@email.com",
365
+ password: "somepassword"
366
+ }
367
+ }).
368
+ on(:user)
369
+
370
+ should redirect_to("/examples")
371
+ should_not redirect_to("/something_else")
372
+
373
+ should set_flash[:success].to("Example created")
374
+ should_not set_flash[:success].to("Something else")
375
+
376
+ should set_session[:some_key].to("some value")
377
+ should_not set_session[:some_key].to("some other value")
378
+ end
379
+ end
380
+ FILE
381
+
382
+ result = app.run_n_unit_test_suite
383
+
384
+ assert_accepts indicate_that_tests_were_run(failures: 0), result
385
+ end
386
+
387
+ def test_failing_assertions_for_active_model
388
+ app.write_file 'test/models/person_test.rb', <<-FILE
389
+ require 'test_helper'
390
+
391
+ class PersonTest < ActiveSupport::TestCase
392
+ # Note: All of these matchers are listed in alphabetical order so we can
393
+ # compare with what is listed inside of the shoulda-matchers README
394
+
395
+ should_not allow_value("john@smith.com").for(:email)
396
+ should allow_value("john").for(:email)
397
+
398
+ # FIXME: See #1187 in shoulda-matchers
399
+ #should_not have_secure_password
400
+
401
+ should_not validate_absence_of(:nothing)
402
+ should validate_absence_of(:some_other_attribute)
403
+
404
+ should_not validate_acceptance_of(:terms_of_service)
405
+ should validate_acceptance_of(:some_other_attribute)
406
+
407
+ should_not validate_confirmation_of(:password)
408
+ should validate_confirmation_of(:some_other_attribute)
409
+
410
+ should_not validate_exclusion_of(:workouts).in_array(["biceps"])
411
+ should validate_exclusion_of(:some_other_attribute).
412
+ in_array(["whatever"])
413
+
414
+ should_not validate_inclusion_of(:foods).in_array(["spaghetti"])
415
+ should validate_inclusion_of(:some_other_attribute).
416
+ in_array(["whatever"])
417
+
418
+ should_not validate_length_of(:card_number).is_at_most(16)
419
+ should validate_length_of(:some_other_attribute).is_at_most(16)
420
+
421
+ should_not validate_numericality_of(:age)
422
+ should validate_numericality_of(:some_other_attribute)
423
+
424
+ should_not validate_presence_of(:something)
425
+ should validate_presence_of(:some_other_attribute)
426
+
427
+ should_not delegate_method(:a_method).to(:some_delegate_object)
428
+ should delegate_method(:some_other_method).to(:some_other_object)
429
+ end
430
+ FILE
431
+
432
+ result = app.run_n_unit_test_suite
433
+
434
+ assert_accepts indicate_that_tests_were_run(failures: 20), result
435
+ end
436
+
437
+ def test_failing_assertions_for_active_record
438
+ app.write_file 'test/models/user_test.rb', <<-FILE
439
+ require 'test_helper'
440
+
441
+ class UserTest < ActiveSupport::TestCase
442
+ # Note: All of these matchers are listed in alphabetical order so we can
443
+ # compare with what is listed inside of the shoulda-matchers README
444
+
445
+ should_not belong_to(:city)
446
+ should belong_to(:some_other_attribute)
447
+
448
+ should_not define_enum_for(:status).with_values(inactive: 0, active: 1)
449
+ should define_enum_for(:status).with_values(foo: "bar")
450
+
451
+ should_not have_db_column(:age)
452
+ should have_db_column(:some_other_attribute)
453
+
454
+ should_not have_db_index(:account_id)
455
+ should have_db_index(:some_other_attribute)
456
+
457
+ should_not have_readonly_attribute(:username)
458
+ should have_readonly_attribute(:some_other_attribute)
459
+
460
+ should_not have_and_belong_to_many(:categories)
461
+ should have_and_belong_to_many(:whatevers)
462
+
463
+ should_not have_many(:issues)
464
+ should have_many(:whatevers)
465
+
466
+ should_not have_one(:life)
467
+ should have_one(:whatever)
468
+
469
+ should_not serialize(:aspects)
470
+ should serialize(:age)
471
+
472
+ should_not validate_uniqueness_of(:email)
473
+ should validate_uniqueness_of(:some_other_attribute)
474
+
475
+ should_not accept_nested_attributes_for(:issues)
476
+ should accept_nested_attributes_for(:some_other_attribute)
477
+ end
478
+ FILE
479
+
480
+ result = app.run_n_unit_test_suite
481
+
482
+ assert_accepts indicate_that_tests_were_run(failures: 22), result
483
+ end
484
+
485
+ def test_failing_assertions_for_action_controller
486
+ app.write_file 'test/controllers/examples_controller_test.rb', <<-FILE
487
+ require 'test_helper'
488
+
489
+ class ExamplesControllerTest < ActionController::TestCase
490
+ context "GET #index" do
491
+ setup do
492
+ get :index
493
+ end
494
+
495
+ # Note: All of these matchers are listed in alphabetical order so we
496
+ # can compare with what is listed inside of the shoulda-matchers
497
+ # README
498
+
499
+ should_not use_before_action(:some_before_action)
500
+ should use_before_action(:some_other_before_action)
501
+ should_not use_after_action(:some_after_action)
502
+ should use_after_action(:some_other_after_action)
503
+ should_not use_around_action(:some_around_action)
504
+ should use_around_action(:some_other_around_action)
505
+
506
+ should_not filter_param(:password)
507
+ should filter_param(:some_other_param)
508
+
509
+ should_not rescue_from(ActiveRecord::RecordNotFound).
510
+ with(:handle_not_found)
511
+ should rescue_from(ActiveRecord::RecordNotFound).
512
+ with(:some_other_method)
513
+
514
+ should_not render_template(:index)
515
+ should render_template(:some_other_action)
516
+
517
+ should_not render_with_layout("application")
518
+ should render_with_layout("some_other_layout")
519
+
520
+ should_not respond_with(:ok)
521
+ should respond_with(:some_other_status)
522
+
523
+ should_not route(:get, "/examples").to(action: :index)
524
+ should route(:get, "/examples").to(action: :something_else)
525
+ end
526
+
527
+ context "POST #create" do
528
+ setup do
529
+ if ActionPack::VERSION::STRING.start_with?("4.")
530
+ post :create, {
531
+ user: {
532
+ email: "some@email.com",
533
+ password: "somepassword"
534
+ }
535
+ }
536
+ else
537
+ post :create, params: {
538
+ user: {
539
+ email: "some@email.com",
540
+ password: "somepassword"
541
+ }
542
+ }
543
+ end
544
+ end
545
+
546
+ should_not permit(:email, :password).
547
+ for(:create, params: {
548
+ user: {
549
+ email: "some@email.com",
550
+ password: "somepassword"
551
+ }
552
+ }).
553
+ on(:user)
554
+ should permit(:foo, :bar).
555
+ for(:create, params: {
556
+ user: {
557
+ email: "some@email.com",
558
+ password: "somepassword"
559
+ }
560
+ }).
561
+ on(:user)
562
+
563
+ should_not redirect_to("/examples")
564
+ should redirect_to("/something_else")
565
+
566
+ should_not set_flash[:success].to("Example created")
567
+ should set_flash[:success].to("Something else")
568
+
569
+ should_not set_session[:some_key].to("some value")
570
+ should set_session[:some_key].to("some other value")
571
+ end
572
+ end
573
+ FILE
574
+
575
+ result = app.run_n_unit_test_suite
576
+
577
+ assert_accepts indicate_that_tests_were_run(failures: 26), result
578
+ end
579
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
580
+ end