shoulda-matchers 3.1.3 → 4.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/.hound/ruby.yml +336 -316
  3. data/.python-version +1 -0
  4. data/.rubocop.yml +3 -1
  5. data/.travis.yml +7 -6
  6. data/Appraisals +76 -44
  7. data/CONTRIBUTING.md +137 -66
  8. data/Gemfile +5 -5
  9. data/Gemfile.lock +30 -35
  10. data/MAINTAINING.md +250 -0
  11. data/MIT-LICENSE +1 -1
  12. data/NEWS.md +176 -4
  13. data/README.md +138 -200
  14. data/Rakefile +7 -0
  15. data/bin/setup +190 -0
  16. data/doc_config/yard/templates/default/fulldoc/html/css/global.css +4 -0
  17. data/doc_config/yard/templates/default/fulldoc/html/full_list.erb +0 -6
  18. data/doc_config/yard/templates/default/fulldoc/html/js/app.js +0 -17
  19. data/doc_config/yard/templates/default/fulldoc/html/setup.rb +27 -0
  20. data/gemfiles/4.2.gemfile +21 -20
  21. data/gemfiles/4.2.gemfile.lock +143 -140
  22. data/gemfiles/5.0.gemfile +37 -0
  23. data/gemfiles/5.0.gemfile.lock +238 -0
  24. data/gemfiles/5.1.gemfile +38 -0
  25. data/gemfiles/5.1.gemfile.lock +254 -0
  26. data/gemfiles/5.2.gemfile +40 -0
  27. data/gemfiles/5.2.gemfile.lock +273 -0
  28. data/lib/shoulda/matchers/action_controller/callback_matcher.rb +18 -6
  29. data/lib/shoulda/matchers/action_controller/permit_matcher.rb +6 -1
  30. data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +1 -1
  31. data/lib/shoulda/matchers/action_controller/route_matcher.rb +87 -27
  32. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +1 -0
  33. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter.rb +0 -4
  34. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +5 -0
  35. data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +5 -0
  36. data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +26 -11
  37. data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +39 -4
  38. data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +116 -47
  39. data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +127 -38
  40. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +55 -37
  41. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +30 -1
  42. data/lib/shoulda/matchers/active_model/validation_matcher.rb +11 -4
  43. data/lib/shoulda/matchers/active_model/validation_matcher/build_description.rb +11 -6
  44. data/lib/shoulda/matchers/active_record.rb +3 -0
  45. data/lib/shoulda/matchers/active_record/association_matcher.rb +172 -22
  46. data/lib/shoulda/matchers/active_record/association_matchers/join_table_matcher.rb +1 -1
  47. data/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +11 -6
  48. data/lib/shoulda/matchers/active_record/association_matchers/optional_matcher.rb +46 -0
  49. data/lib/shoulda/matchers/active_record/association_matchers/required_matcher.rb +51 -0
  50. data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +268 -38
  51. data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +1 -1
  52. data/lib/shoulda/matchers/active_record/have_secure_token_matcher.rb +111 -0
  53. data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +207 -79
  54. data/lib/shoulda/matchers/doublespeak/object_double.rb +5 -1
  55. data/lib/shoulda/matchers/independent/delegate_method_matcher.rb +100 -21
  56. data/lib/shoulda/matchers/rails_shim.rb +133 -52
  57. data/lib/shoulda/matchers/routing.rb +2 -2
  58. data/lib/shoulda/matchers/util.rb +23 -1
  59. data/lib/shoulda/matchers/util/word_wrap.rb +6 -2
  60. data/lib/shoulda/matchers/version.rb +1 -1
  61. data/script/install_gems_in_all_appraisals +3 -1
  62. data/script/run_all_tests +3 -1
  63. data/script/supported_ruby_versions +7 -0
  64. data/script/update_gem_in_all_appraisals +3 -1
  65. data/script/update_gems_in_all_appraisals +3 -1
  66. data/shoulda-matchers.gemspec +3 -3
  67. data/spec/acceptance/independent_matchers_spec.rb +2 -2
  68. data/spec/acceptance/multiple_libraries_integration_spec.rb +1 -1
  69. data/spec/acceptance/rails_integration_spec.rb +2 -2
  70. data/spec/spec_helper.rb +2 -3
  71. data/spec/support/acceptance/helpers.rb +2 -0
  72. data/spec/support/acceptance/helpers/command_helpers.rb +17 -4
  73. data/spec/support/acceptance/helpers/rails_migration_helpers.rb +21 -0
  74. data/spec/support/acceptance/helpers/step_helpers.rb +1 -1
  75. data/spec/support/tests/current_bundle.rb +3 -9
  76. data/spec/support/tests/filesystem.rb +2 -2
  77. data/spec/support/unit/attribute.rb +0 -2
  78. data/spec/support/unit/capture.rb +9 -3
  79. data/spec/support/unit/helpers/action_pack_versions.rb +22 -0
  80. data/spec/support/unit/helpers/active_model_versions.rb +4 -0
  81. data/spec/support/unit/helpers/active_record_versions.rb +22 -2
  82. data/spec/support/unit/helpers/active_resource_builder.rb +2 -2
  83. data/spec/support/unit/helpers/controller_builder.rb +1 -1
  84. data/spec/support/unit/helpers/message_helpers.rb +19 -0
  85. data/spec/support/unit/helpers/rails_versions.rb +14 -0
  86. data/spec/support/unit/matchers/fail_with_message_matcher.rb +7 -5
  87. data/spec/support/unit/matchers/print_warning_including.rb +21 -13
  88. data/spec/support/unit/model_creation_strategies/active_record.rb +1 -1
  89. data/spec/support/unit/model_creators/active_record.rb +0 -1
  90. data/spec/support/unit/model_creators/basic.rb +7 -2
  91. data/spec/support/unit/rails_application.rb +25 -0
  92. data/spec/support/unit/record_validating_confirmation_builder.rb +5 -2
  93. data/spec/support/unit/validation_matcher_scenario.rb +0 -2
  94. data/spec/unit/shoulda/matchers/action_controller/callback_matcher_spec.rb +18 -18
  95. data/spec/unit/shoulda/matchers/action_controller/permit_matcher_spec.rb +33 -5
  96. data/spec/unit/shoulda/matchers/action_controller/render_template_matcher_spec.rb +1 -1
  97. data/spec/unit/shoulda/matchers/active_model/allow_mass_assignment_of_matcher_spec.rb +80 -78
  98. data/spec/unit/shoulda/matchers/active_model/allow_value_matcher_spec.rb +7 -9
  99. data/spec/unit/shoulda/matchers/active_model/validate_absence_of_matcher_spec.rb +28 -4
  100. data/spec/unit/shoulda/matchers/active_model/validate_acceptance_of_matcher_spec.rb +19 -1
  101. data/spec/unit/shoulda/matchers/active_model/validate_confirmation_of_matcher_spec.rb +27 -4
  102. data/spec/unit/shoulda/matchers/active_model/validate_exclusion_of_matcher_spec.rb +62 -5
  103. data/spec/unit/shoulda/matchers/active_model/validate_inclusion_of_matcher_spec.rb +52 -18
  104. data/spec/unit/shoulda/matchers/active_model/validate_length_of_matcher_spec.rb +51 -4
  105. data/spec/unit/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +99 -71
  106. data/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +41 -15
  107. data/spec/unit/shoulda/matchers/active_record/association_matcher_spec.rb +445 -15
  108. data/spec/unit/shoulda/matchers/active_record/define_enum_for_matcher_spec.rb +615 -93
  109. data/spec/unit/shoulda/matchers/active_record/have_secure_token_matcher_spec.rb +169 -0
  110. data/spec/unit/shoulda/matchers/active_record/validate_uniqueness_of_matcher_spec.rb +167 -97
  111. data/spec/unit/shoulda/matchers/doublespeak/world_spec.rb +2 -4
  112. data/spec/unit/shoulda/matchers/independent/delegate_method_matcher_spec.rb +152 -19
  113. data/spec/unit/shoulda/matchers/routing/route_matcher_spec.rb +258 -94
  114. data/spec/unit_spec_helper.rb +9 -1
  115. data/zeus.json +1 -1
  116. metadata +31 -16
  117. data/gemfiles/4.0.0.gemfile +0 -38
  118. data/gemfiles/4.0.0.gemfile.lock +0 -223
  119. data/gemfiles/4.0.1.gemfile +0 -38
  120. data/gemfiles/4.0.1.gemfile.lock +0 -225
  121. data/gemfiles/4.1.gemfile +0 -38
  122. data/gemfiles/4.1.gemfile.lock +0 -220
  123. data/script/SUPPORTED_VERSIONS +0 -1
@@ -33,10 +33,8 @@ module Shoulda::Matchers::Doublespeak
33
33
  block_called = false
34
34
  double_collections = Array.new(3) { build_double_collection }
35
35
  double_collections.each do |double_collection|
36
- allow(double_collection).to receive(:activate).ordered
37
- end
38
- double_collections.each do |double_collection|
39
- allow(double_collection).to receive(:deactivate).ordered
36
+ allow(double_collection).to receive(:activate)
37
+ allow(double_collection).to receive(:deactivate)
40
38
  end
41
39
  klasses = Array.new(3) { |i| "Klass #{i}" }
42
40
  world = described_class.new
@@ -8,7 +8,7 @@ describe Shoulda::Matchers::Independent::DelegateMethodMatcher do
8
8
  context 'without any qualifiers' do
9
9
  it 'states that it should delegate method to the right object' do
10
10
  matcher = delegate_method(:method_name).to(:delegate)
11
- message = 'delegate #method_name to #delegate object'
11
+ message = 'delegate #method_name to the #delegate object'
12
12
 
13
13
  expect(matcher.description).to eq message
14
14
  end
@@ -17,7 +17,7 @@ describe Shoulda::Matchers::Independent::DelegateMethodMatcher do
17
17
  context 'qualified with #as' do
18
18
  it 'states that it should delegate method to the right object and method' do
19
19
  matcher = delegate_method(:method_name).to(:delegate).as(:alternate)
20
- message = 'delegate #method_name to #delegate object as #alternate'
20
+ message = 'delegate #method_name to the #delegate object as #alternate'
21
21
 
22
22
  expect(matcher.description).to eq message
23
23
  end
@@ -27,7 +27,7 @@ describe Shoulda::Matchers::Independent::DelegateMethodMatcher do
27
27
  it 'states that it should delegate method to the right object with right argument' do
28
28
  matcher = delegate_method(:method_name).to(:delegate).
29
29
  with_arguments(:foo, bar: [1, 2])
30
- message = 'delegate #method_name to #delegate object passing arguments [:foo, {:bar=>[1, 2]}]'
30
+ message = 'delegate #method_name to the #delegate object passing arguments [:foo, {:bar=>[1, 2]}]'
31
31
 
32
32
  expect(matcher.description).to eq message
33
33
  end
@@ -53,7 +53,7 @@ describe Shoulda::Matchers::Independent::DelegateMethodMatcher do
53
53
 
54
54
  matcher = delegate_method(:hello).to(:country).with_prefix
55
55
  expect(matcher.description).
56
- to eq('delegate #country_hello to #country object as #hello')
56
+ to eq('delegate #country_hello to the #country object as #hello')
57
57
  end
58
58
  end
59
59
  end
@@ -77,7 +77,7 @@ describe Shoulda::Matchers::Independent::DelegateMethodMatcher do
77
77
 
78
78
  matcher = delegate_method(:hello).to(:country).with_prefix(true)
79
79
  expect(matcher.description).
80
- to eq('delegate #country_hello to #country object as #hello')
80
+ to eq('delegate #country_hello to the #country object as #hello')
81
81
  end
82
82
  end
83
83
  end
@@ -98,7 +98,7 @@ describe Shoulda::Matchers::Independent::DelegateMethodMatcher do
98
98
 
99
99
  matcher = delegate_method(:hello).to(:country).with_prefix('county')
100
100
  expect(matcher.description).
101
- to eq('delegate #county_hello to #country object as #hello')
101
+ to eq('delegate #county_hello to the #country object as #hello')
102
102
  end
103
103
  end
104
104
  end
@@ -112,14 +112,14 @@ describe Shoulda::Matchers::Independent::DelegateMethodMatcher do
112
112
  matcher = delegate_method(:method_name).to(:delegate)
113
113
 
114
114
  expect(matcher.description).
115
- to eq 'delegate .method_name to .delegate object'
115
+ to eq 'delegate .method_name to the .delegate object'
116
116
  end
117
117
  end
118
118
 
119
119
  context 'qualified with #as' do
120
120
  it 'states that it should delegate method to the right object and method' do
121
121
  matcher = delegate_method(:method_name).to(:delegate).as(:alternate)
122
- message = 'delegate .method_name to .delegate object as .alternate'
122
+ message = 'delegate .method_name to the .delegate object as .alternate'
123
123
 
124
124
  expect(matcher.description).to eq message
125
125
  end
@@ -129,7 +129,7 @@ describe Shoulda::Matchers::Independent::DelegateMethodMatcher do
129
129
  it 'states that it should delegate method to the right object with right argument' do
130
130
  matcher = delegate_method(:method_name).to(:delegate).
131
131
  with_arguments(:foo, bar: [1, 2])
132
- message = 'delegate .method_name to .delegate object passing arguments [:foo, {:bar=>[1, 2]}]'
132
+ message = 'delegate .method_name to the .delegate object passing arguments [:foo, {:bar=>[1, 2]}]'
133
133
 
134
134
  expect(matcher.description).to eq message
135
135
  end
@@ -178,7 +178,8 @@ describe Shoulda::Matchers::Independent::DelegateMethodMatcher do
178
178
  it 'rejects with the correct failure message' do
179
179
  post_office = PostOffice.new
180
180
  message = [
181
- 'Expected PostOffice to delegate #deliver_mail to #mailman object',
181
+ 'Expected PostOffice to delegate #deliver_mail to the #mailman object.',
182
+ '',
182
183
  'Method calls sent to PostOffice#mailman: (none)'
183
184
  ].join("\n")
184
185
 
@@ -191,7 +192,8 @@ describe Shoulda::Matchers::Independent::DelegateMethodMatcher do
191
192
  context 'when the subject is a class' do
192
193
  it 'uses the proper syntax for class methods in errors' do
193
194
  message = [
194
- 'Expected PostOffice to delegate .deliver_mail to .mailman object',
195
+ 'Expected PostOffice to delegate .deliver_mail to the .mailman object.',
196
+ '',
195
197
  'Method calls sent to PostOffice.mailman: (none)'
196
198
  ].join("\n")
197
199
 
@@ -225,7 +227,7 @@ describe Shoulda::Matchers::Independent::DelegateMethodMatcher do
225
227
  context 'negating the matcher' do
226
228
  it 'rejects with the correct failure message' do
227
229
  post_office = PostOffice.new
228
- message = 'Expected PostOffice not to delegate #deliver_mail to #mailman object, but it did'
230
+ message = 'Expected PostOffice not to delegate #deliver_mail to the #mailman object, but it did.'
229
231
 
230
232
  expect {
231
233
  expect(post_office).not_to delegate_method(:deliver_mail).to(:mailman)
@@ -283,7 +285,7 @@ describe Shoulda::Matchers::Independent::DelegateMethodMatcher do
283
285
  context 'negating the matcher' do
284
286
  it 'rejects with the correct failure message' do
285
287
  post_office = PostOffice.new
286
- message = 'Expected PostOffice not to delegate #deliver_mail to #mailman object passing arguments ["221B Baker St.", {:hastily=>true}], but it did'
288
+ message = 'Expected PostOffice not to delegate #deliver_mail to the #mailman object passing arguments ["221B Baker St.", {:hastily=>true}], but it did.'
287
289
 
288
290
  expect {
289
291
  expect(post_office).
@@ -299,8 +301,11 @@ describe Shoulda::Matchers::Independent::DelegateMethodMatcher do
299
301
  it 'rejects with the correct failure message' do
300
302
  post_office = PostOffice.new
301
303
  message = [
302
- 'Expected PostOffice to delegate #deliver_mail to #mailman object passing arguments ["123 Nowhere Ln."]',
304
+ 'Expected PostOffice to delegate #deliver_mail to the #mailman object',
305
+ 'passing arguments ["123 Nowhere Ln."].',
306
+ '',
303
307
  'Method calls sent to PostOffice#mailman:',
308
+ '',
304
309
  '1) deliver_mail("221B Baker St.", {:hastily=>true})'
305
310
  ].join("\n")
306
311
 
@@ -338,7 +343,7 @@ describe Shoulda::Matchers::Independent::DelegateMethodMatcher do
338
343
  context 'negating the assertion' do
339
344
  it 'rejects with the correct failure message' do
340
345
  post_office = PostOffice.new
341
- message = 'Expected PostOffice not to delegate #deliver_mail to #mailman object as #deliver_mail_and_avoid_dogs, but it did'
346
+ message = 'Expected PostOffice not to delegate #deliver_mail to the #mailman object as #deliver_mail_and_avoid_dogs, but it did.'
342
347
 
343
348
  expect {
344
349
  expect(post_office).
@@ -354,8 +359,11 @@ describe Shoulda::Matchers::Independent::DelegateMethodMatcher do
354
359
  it 'rejects with the correct failure message' do
355
360
  post_office = PostOffice.new
356
361
  message = [
357
- 'Expected PostOffice to delegate #deliver_mail to #mailman object as #watch_tv',
362
+ 'Expected PostOffice to delegate #deliver_mail to the #mailman object as',
363
+ '#watch_tv.',
364
+ '',
358
365
  'Method calls sent to PostOffice#mailman:',
366
+ '',
359
367
  '1) deliver_mail_and_avoid_dogs()'
360
368
  ].join("\n")
361
369
 
@@ -401,7 +409,9 @@ describe Shoulda::Matchers::Independent::DelegateMethodMatcher do
401
409
  end
402
410
 
403
411
  message = [
404
- 'Expected Person to delegate #country_hello to #country object as #hello',
412
+ 'Expected Person to delegate #country_hello to the #country object as',
413
+ '#hello.',
414
+ '',
405
415
  'Method calls sent to Person#country: (none)'
406
416
  ].join("\n")
407
417
 
@@ -449,7 +459,9 @@ describe Shoulda::Matchers::Independent::DelegateMethodMatcher do
449
459
  end
450
460
 
451
461
  message = [
452
- 'Expected Person to delegate #country_hello to #country object as #hello',
462
+ 'Expected Person to delegate #country_hello to the #country object as',
463
+ '#hello.',
464
+ '',
453
465
  'Method calls sent to Person#country: (none)'
454
466
  ].join("\n")
455
467
 
@@ -499,7 +511,9 @@ describe Shoulda::Matchers::Independent::DelegateMethodMatcher do
499
511
  end
500
512
 
501
513
  message = [
502
- 'Expected Person to delegate #county_hello to #country object as #hello',
514
+ 'Expected Person to delegate #county_hello to the #country object as',
515
+ '#hello.',
516
+ '',
503
517
  'Method calls sent to Person#country: (none)'
504
518
  ].join("\n")
505
519
 
@@ -514,4 +528,123 @@ describe Shoulda::Matchers::Independent::DelegateMethodMatcher do
514
528
  end
515
529
  end
516
530
  end
531
+
532
+ context 'qualified with #allow_nil' do
533
+ context 'when using delegate from Rails' do
534
+ context 'when delegations were defined with :allow_nil' do
535
+ it 'accepts' do
536
+ define_class('Person') do
537
+ delegate :hello, to: :country, allow_nil: true
538
+ def country; end
539
+ end
540
+
541
+ person = Person.new
542
+
543
+ expect(person).to delegate_method(:hello).to(:country).allow_nil
544
+ end
545
+ end
546
+
547
+ context 'when delegations were not defined with :allow_nil' do
548
+ it 'rejects with the correct failure message' do
549
+ define_class('Person') do
550
+ delegate :hello, to: :country
551
+ def country; end
552
+ end
553
+
554
+ person = Person.new
555
+
556
+ message = <<-MESSAGE
557
+ Expected Person to delegate #hello to the #country object, allowing
558
+ #country to return nil.
559
+
560
+ Person#hello did delegate to #country when it was non-nil, but it failed
561
+ to account for when #country *was* nil.
562
+ MESSAGE
563
+
564
+ expectation = lambda do
565
+ expect(person).to delegate_method(:hello).to(:country).allow_nil
566
+ end
567
+
568
+ expect(&expectation).to fail_with_message(message)
569
+ end
570
+ end
571
+ end
572
+
573
+ context 'when using Forwardable' do
574
+ context 'when the delegate object is nil' do
575
+ it 'rejects with the correct failure message' do
576
+ define_class('Person') do
577
+ extend Forwardable
578
+
579
+ def_delegators :country, :hello
580
+
581
+ def country; end
582
+ end
583
+
584
+ person = Person.new
585
+
586
+ message = <<-MESSAGE
587
+ Expected Person to delegate #hello to the #country object, allowing
588
+ #country to return nil.
589
+
590
+ Person#hello did delegate to #country when it was non-nil, but it failed
591
+ to account for when #country *was* nil.
592
+ MESSAGE
593
+
594
+ expectation = lambda do
595
+ expect(person).to delegate_method(:hello).to(:country).allow_nil
596
+ end
597
+
598
+ expect(&expectation).to fail_with_message(message)
599
+ end
600
+ end
601
+ end
602
+
603
+ context 'when delegating manually' do
604
+ context 'when the delegating method accounts for the delegate object being nil' do
605
+ it 'accepts' do
606
+ define_class('Person') do
607
+ def country; end
608
+
609
+ def hello
610
+ return unless country
611
+ country.hello
612
+ end
613
+ end
614
+
615
+ person = Person.new
616
+
617
+ expect(person).to delegate_method(:hello).to(:country).allow_nil
618
+ end
619
+ end
620
+
621
+ context 'when the delegating method does not account for the delegate object being nil' do
622
+ it 'rejects with the correct failure message' do
623
+ define_class('Person') do
624
+ def country; end
625
+
626
+ def hello
627
+ country.hello
628
+ end
629
+ end
630
+
631
+ person = Person.new
632
+
633
+ message = <<-MESSAGE
634
+ Expected Person to delegate #hello to the #country object, allowing
635
+ #country to return nil.
636
+
637
+ Person#hello did delegate to #country when it was non-nil, but it failed
638
+ to account for when #country *was* nil.
639
+ MESSAGE
640
+
641
+ expectation = lambda do
642
+ expect(person).to delegate_method(:hello).to(:country).allow_nil
643
+ end
644
+
645
+ expect(&expectation).to fail_with_message(message)
646
+ end
647
+ end
648
+ end
649
+ end
517
650
  end
@@ -1,146 +1,222 @@
1
1
  require 'unit_spec_helper'
2
2
 
3
3
  describe 'Shoulda::Matchers::Routing::RouteMatcher', type: :routing do
4
- before do
5
- define_controller('ThingsController')
6
- end
7
-
8
4
  shared_examples_for 'core tests' do
9
5
  context 'when the given method, path, controller, and action match an existing route' do
10
6
  it 'accepts' do
11
- define_routes { get '/', to: 'things#index' }
7
+ define_controller_and_routes(
8
+ method: :get,
9
+ path: '/',
10
+ controller: 'things',
11
+ action: 'index',
12
+ )
12
13
 
13
- assert_accepts add_target_to(
14
- route(:get, '/'),
14
+ matcher = build_route_matcher(
15
+ method: :get,
16
+ path: '/',
15
17
  controller: 'things',
16
18
  action: 'index'
17
19
  )
20
+
21
+ is_expected.to(matcher)
18
22
  end
19
23
 
20
24
  context 'and the expected controller is specified as a symbol' do
21
25
  it 'accepts' do
22
- define_routes { get '/', to: 'things#index' }
26
+ define_controller_and_routes(
27
+ method: :get,
28
+ path: '/',
29
+ controller: 'things',
30
+ action: 'index',
31
+ )
23
32
 
24
- assert_accepts add_target_to(
25
- route(:get, '/'),
33
+ matcher = build_route_matcher(
34
+ method: :get,
35
+ path: '/',
26
36
  controller: :things,
27
37
  action: 'index'
28
38
  )
39
+
40
+ is_expected.to(matcher)
29
41
  end
30
42
  end
31
43
 
32
44
  context 'and the expected action is specified as a symbol' do
33
45
  it 'accepts' do
34
- define_routes { get '/', to: 'things#index' }
46
+ define_controller_and_routes(
47
+ method: :get,
48
+ path: '/',
49
+ controller: 'things',
50
+ action: 'index',
51
+ )
35
52
 
36
- assert_accepts add_target_to(
37
- route(:get, '/'),
53
+ matcher = build_route_matcher(
54
+ method: :get,
55
+ path: '/',
38
56
  controller: 'things',
39
- action: :index
57
+ action: :index,
40
58
  )
59
+
60
+ is_expected.to(matcher)
41
61
  end
42
62
  end
43
63
  end
44
64
 
45
65
  context 'when the given method, path, controller, and action do not match an existing route' do
46
66
  it 'rejects' do
47
- assert_rejects add_target_to(
48
- route(:get, '/non_existent_route'),
49
- controller: 'no_controller',
50
- action: 'no_action'
67
+ matcher = build_route_matcher(
68
+ method: :get,
69
+ path: '/non_existent_route',
70
+ controller: 'some_controller',
71
+ action: 'some_action',
51
72
  )
73
+
74
+ is_expected.not_to(matcher)
52
75
  end
53
76
  end
54
77
 
55
78
  context 'when the given path, controller, and action match an existing route but the method does not' do
56
79
  it 'rejects' do
57
- define_routes { post '/', to: 'things#index' }
80
+ define_controller_and_routes(
81
+ method: :get,
82
+ path: '/',
83
+ controller: 'things',
84
+ action: 'index',
85
+ )
58
86
 
59
- assert_rejects add_target_to(
60
- route(:get, '/'),
87
+ matcher = build_route_matcher(
88
+ method: :post,
89
+ path: '/',
61
90
  controller: 'things',
62
- action: 'index'
91
+ action: 'index',
63
92
  )
93
+
94
+ is_expected.not_to(matcher)
64
95
  end
65
96
  end
66
97
 
67
98
  context 'when the given method, controller, and action match an existing route but the path does not' do
68
99
  it 'rejects' do
69
- define_routes { get '/', to: 'things#index' }
100
+ define_controller_and_routes(
101
+ method: :get,
102
+ path: '/',
103
+ controller: 'things',
104
+ action: 'index',
105
+ )
70
106
 
71
- assert_rejects add_target_to(
72
- route(:get, '/different_path'),
107
+ matcher = build_route_matcher(
108
+ method: :get,
109
+ path: '/something_else',
73
110
  controller: 'things',
74
- action: 'index'
111
+ action: 'index',
75
112
  )
113
+
114
+ is_expected.not_to(matcher)
76
115
  end
77
116
  end
78
117
 
79
118
  context 'when the given method and path match an existing route but the controller does not' do
80
119
  it 'rejects' do
81
- define_routes { get '/', to: 'another_controller#index' }
82
-
83
- assert_rejects add_target_to(
84
- route(:get, '/'),
120
+ define_controller_and_routes(
121
+ method: :get,
122
+ path: '/',
85
123
  controller: 'things',
86
- action: 'index'
124
+ action: 'index',
87
125
  )
126
+
127
+ matcher = build_route_matcher(
128
+ method: :get,
129
+ path: '/',
130
+ controller: 'some_other_controller',
131
+ action: 'index',
132
+ )
133
+
134
+ is_expected.not_to(matcher)
88
135
  end
89
136
  end
90
137
 
91
138
  context 'when the given method, path, and controller match an existing route but the action does not' do
92
139
  it 'rejects' do
93
- define_routes { get '/', to: 'things#index' }
140
+ define_controller_and_routes(
141
+ method: :get,
142
+ path: '/',
143
+ controller: 'things',
144
+ action: 'index',
145
+ )
94
146
 
95
- assert_rejects add_target_to(
96
- route(:get, '/'),
147
+ matcher = build_route_matcher(
148
+ method: :get,
149
+ path: '/',
97
150
  controller: 'things',
98
- action: 'another_action'
151
+ action: 'another_action',
99
152
  )
153
+
154
+ is_expected.not_to(matcher)
100
155
  end
101
156
  end
102
157
 
103
158
  context 'when the actual route has a param' do
104
159
  context 'and the expected params include that param' do
105
160
  it 'accepts' do
106
- define_routes { get '/things/:id', to: 'things#show' }
161
+ define_controller_and_routes(
162
+ method: :get,
163
+ path: '/things/:id',
164
+ controller: 'things',
165
+ action: 'show',
166
+ )
107
167
 
108
- assert_accepts add_target_to(
109
- route(:get, '/things/1'),
168
+ matcher = build_route_matcher(
169
+ method: :get,
170
+ path: '/things/1',
110
171
  controller: 'things',
111
172
  action: 'show',
112
- id: '1'
173
+ id: '1',
113
174
  )
175
+
176
+ is_expected.to(matcher)
114
177
  end
115
178
 
116
179
  context 'but its value was not specified as a string' do
117
180
  it 'accepts, treating it as a string' do
118
- define_routes { get '/things/:id', to: 'things#show' }
181
+ define_controller_and_routes(
182
+ method: :get,
183
+ path: '/things/:id',
184
+ controller: 'things',
185
+ action: 'show',
186
+ )
119
187
 
120
- assert_accepts add_target_to(
121
- route(:get, '/things/1'),
188
+ matcher = build_route_matcher(
189
+ method: :get,
190
+ path: '/things/1',
122
191
  controller: 'things',
123
192
  action: 'show',
124
- id: 1
193
+ id: 1,
125
194
  )
195
+
196
+ is_expected.to(matcher)
126
197
  end
127
198
  end
128
199
  end
129
200
 
130
201
  context 'and the expected params do not match the actual params' do
131
202
  it 'rejects' do
132
- define_routes { get '/things/:id', to: 'things#show' }
203
+ define_controller_and_routes(
204
+ method: :get,
205
+ path: '/things/:id',
206
+ controller: 'things',
207
+ action: 'show',
208
+ )
133
209
 
134
- params = {
210
+ matcher = build_route_matcher(
211
+ method: :get,
212
+ path: '/things/1',
135
213
  controller: 'things',
136
214
  action: 'show',
137
215
  some: 'other',
138
- params: 'here'
139
- }
140
- assert_rejects add_target_to(
141
- route(:get, '/things/:id'),
142
- params
216
+ params: 'here',
143
217
  )
218
+
219
+ is_expected.not_to(matcher)
144
220
  end
145
221
  end
146
222
  end
@@ -149,35 +225,45 @@ describe 'Shoulda::Matchers::Routing::RouteMatcher', type: :routing do
149
225
  context 'and the expected params include a value for it' do
150
226
  context 'as a symbol' do
151
227
  it 'accepts' do
152
- define_routes do
153
- post '/things(.:format)',
154
- to: 'things#create',
155
- defaults: { format: :json }
156
- end
157
-
158
- assert_accepts add_target_to(
159
- route(:post, '/things'),
228
+ define_controller_and_routes(
229
+ method: :post,
230
+ path: '/things(.:format)',
231
+ controller: 'things',
232
+ action: 'create',
233
+ defaults: { format: :json },
234
+ )
235
+
236
+ matcher = build_route_matcher(
237
+ method: :post,
238
+ path: '/things',
160
239
  controller: 'things',
161
240
  action: 'create',
162
- format: :json
241
+ format: :json,
163
242
  )
243
+
244
+ is_expected.to(matcher)
164
245
  end
165
246
  end
166
247
 
167
248
  context 'as a string' do
168
249
  it 'accepts' do
169
- define_routes do
170
- post '/things(.:format)',
171
- to: 'things#create',
172
- defaults: { format: :json }
173
- end
174
-
175
- assert_accepts add_target_to(
176
- route(:post, '/things'),
250
+ define_controller_and_routes(
251
+ method: :post,
252
+ path: '/things(.:format)',
253
+ controller: 'things',
254
+ action: 'create',
255
+ defaults: { format: :json },
256
+ )
257
+
258
+ matcher = build_route_matcher(
259
+ method: :post,
260
+ path: '/things',
177
261
  controller: 'things',
178
262
  action: 'create',
179
- format: 'json'
263
+ format: 'json',
180
264
  )
265
+
266
+ is_expected.to(matcher)
181
267
  end
182
268
  end
183
269
  end
@@ -186,57 +272,135 @@ describe 'Shoulda::Matchers::Routing::RouteMatcher', type: :routing do
186
272
  context 'when the existing route has a glob segment' do
187
273
  context 'and a param is given which represents the segment' do
188
274
  it 'accepts' do
189
- define_routes { get '/things/*id', to: 'things#whatever' }
275
+ define_controller_and_routes(
276
+ method: :get,
277
+ path: '/things/*id',
278
+ controller: 'things',
279
+ action: 'show',
280
+ )
190
281
 
191
- assert_accepts add_target_to(
192
- route(:get, '/things/foo/bar'),
282
+ matcher = build_route_matcher(
283
+ method: :get,
284
+ path: '/things/foo/bar',
193
285
  controller: 'things',
194
- action: 'whatever',
195
- id: 'foo/bar'
286
+ action: 'show',
287
+ id: 'foo/bar',
196
288
  )
289
+
290
+ is_expected.to(matcher)
197
291
  end
198
292
  end
199
293
 
200
294
  context 'and no param is given which represents the segment' do
201
295
  it 'rejects' do
202
- define_routes { get '/things/*id', to: 'things#whatever' }
296
+ define_controller_and_routes(
297
+ method: :get,
298
+ path: '/things/*id',
299
+ controller: 'things',
300
+ action: 'show',
301
+ )
203
302
 
204
- assert_rejects add_target_to(
205
- route(:get, '/things'),
303
+ matcher = build_route_matcher(
304
+ method: :get,
305
+ path: '/things',
206
306
  controller: 'things',
207
- action: 'whatever'
307
+ action: 'show',
208
308
  )
309
+
310
+ is_expected.not_to(matcher)
311
+ end
312
+ end
313
+ end
314
+
315
+ context 'when a port is specified' do
316
+ context 'when the route is constrained to the same port' do
317
+ it 'accepts' do
318
+ define_controller_and_routes(
319
+ method: :get,
320
+ path: '/',
321
+ controller: 'things',
322
+ action: 'show',
323
+ constraints: port_constraint_class.new(12345),
324
+ )
325
+
326
+ matcher = build_route_matcher(
327
+ method: :get,
328
+ path: '/',
329
+ controller: 'things',
330
+ action: 'show',
331
+ port: 12345,
332
+ )
333
+
334
+ is_expected.to(matcher)
335
+ end
336
+ end
337
+
338
+ context 'when the route is not constrained to the same port' do
339
+ it 'rejects' do
340
+ define_controller_and_routes(
341
+ method: :get,
342
+ path: '/',
343
+ controller: 'things',
344
+ action: 'show',
345
+ constraints: port_constraint_class.new(12345),
346
+ )
347
+
348
+ matcher = build_route_matcher(
349
+ method: :get,
350
+ path: '/',
351
+ controller: 'things',
352
+ action: 'show',
353
+ port: 99999,
354
+ )
355
+
356
+ is_expected.not_to(matcher)
209
357
  end
210
358
  end
211
359
  end
212
360
  end
213
361
 
214
362
  context 'given a controller and action specified as individual options' do
215
- include_examples 'core tests'
216
-
217
- def add_target_to(route_matcher, params)
218
- route_matcher.to(params)
363
+ include_examples 'core tests' do
364
+ def build_route_matcher(method:, path:, port: nil, **params)
365
+ super(method: method, path: path, port: port).to(params)
366
+ end
219
367
  end
220
368
  end
221
369
 
222
370
  context 'given a controller and action joined together in a string' do
223
- include_examples 'core tests'
224
-
225
- def add_target_to(route_matcher, args)
226
- controller = args.fetch(:controller)
227
- action = args.fetch(:action)
228
- route_matcher.to(
229
- "#{controller}##{action}",
230
- args.except(:controller, :action)
231
- )
371
+ include_examples 'core tests' do
372
+ def build_route_matcher(method:, path:, controller:, action:, port: nil, **rest)
373
+ super(method: method, path: path, port: port).
374
+ to("#{controller}##{action}", **rest)
375
+ end
376
+ end
377
+ end
378
+
379
+ def define_controller_and_routes(method:, path:, controller:, action:, **params)
380
+ define_controller(controller.camelize)
381
+
382
+ define_routes do
383
+ send(method, path, controller: controller, action: action, **params)
232
384
  end
233
385
  end
234
386
 
235
- def assert_accepts(matcher)
236
- should(matcher)
387
+ def build_route_matcher(method:, path:, port:, **)
388
+ route(method, path, port: port)
237
389
  end
238
390
 
239
- def assert_rejects(matcher)
240
- should_not(matcher)
391
+ let(:port_constraint_class) do
392
+ Class.new do
393
+ def initialize(port)
394
+ @port = port
395
+ end
396
+
397
+ def matches?(request)
398
+ request.port == port
399
+ end
400
+
401
+ private
402
+
403
+ attr_reader :port
404
+ end
241
405
  end
242
406
  end