shoulda-matchers 3.0.0.rc1 → 3.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 (106) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -3
  3. data/Gemfile.lock +12 -41
  4. data/NEWS.md +118 -26
  5. data/README.md +34 -11
  6. data/doc_config/yard/templates/default/fulldoc/html/css/bootstrap.css +0 -0
  7. data/doc_config/yard/templates/default/fulldoc/html/css/style.css +4 -0
  8. data/gemfiles/4.0.0.gemfile +2 -3
  9. data/gemfiles/4.0.0.gemfile.lock +47 -77
  10. data/gemfiles/4.0.1.gemfile +2 -3
  11. data/gemfiles/4.0.1.gemfile.lock +51 -79
  12. data/gemfiles/4.1.gemfile +2 -3
  13. data/gemfiles/4.1.gemfile.lock +73 -103
  14. data/gemfiles/4.2.gemfile +2 -3
  15. data/gemfiles/4.2.gemfile.lock +90 -124
  16. data/lib/shoulda/matchers.rb +1 -0
  17. data/lib/shoulda/matchers/action_controller/callback_matcher.rb +6 -8
  18. data/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +1 -3
  19. data/lib/shoulda/matchers/action_controller/flash_store.rb +1 -8
  20. data/lib/shoulda/matchers/action_controller/permit_matcher.rb +140 -88
  21. data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +2 -5
  22. data/lib/shoulda/matchers/action_controller/render_template_matcher.rb +5 -10
  23. data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +2 -4
  24. data/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +1 -3
  25. data/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +3 -5
  26. data/lib/shoulda/matchers/action_controller/route_matcher.rb +5 -7
  27. data/lib/shoulda/matchers/action_controller/set_flash_matcher.rb +35 -9
  28. data/lib/shoulda/matchers/action_controller/set_session_matcher.rb +3 -3
  29. data/lib/shoulda/matchers/active_model.rb +57 -1
  30. data/lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb +2 -5
  31. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +162 -54
  32. data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +5 -2
  33. data/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +1 -3
  34. data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +24 -11
  35. data/lib/shoulda/matchers/active_model/numericality_matchers/even_number_matcher.rb +4 -3
  36. data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +0 -2
  37. data/lib/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher.rb +4 -3
  38. data/lib/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher.rb +2 -1
  39. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +15 -13
  40. data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +3 -3
  41. data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +3 -3
  42. data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +4 -4
  43. data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +8 -8
  44. data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +8 -8
  45. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +12 -14
  46. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +10 -4
  47. data/lib/shoulda/matchers/active_model/validation_matcher.rb +0 -3
  48. data/lib/shoulda/matchers/active_model/validator.rb +0 -8
  49. data/lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb +4 -6
  50. data/lib/shoulda/matchers/active_record/association_matcher.rb +58 -43
  51. data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +2 -2
  52. data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +3 -5
  53. data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +3 -5
  54. data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +1 -4
  55. data/lib/shoulda/matchers/active_record/serialize_matcher.rb +3 -5
  56. data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +7 -7
  57. data/lib/shoulda/matchers/doublespeak/double.rb +10 -1
  58. data/lib/shoulda/matchers/doublespeak/double_collection.rb +13 -5
  59. data/lib/shoulda/matchers/doublespeak/method_call.rb +10 -1
  60. data/lib/shoulda/matchers/doublespeak/object_double.rb +2 -1
  61. data/lib/shoulda/matchers/doublespeak/world.rb +10 -0
  62. data/lib/shoulda/matchers/error.rb +4 -0
  63. data/lib/shoulda/matchers/independent/delegate_method_matcher.rb +11 -10
  64. data/lib/shoulda/matchers/integrations/libraries.rb +1 -0
  65. data/lib/shoulda/matchers/integrations/libraries/action_controller.rb +1 -1
  66. data/lib/shoulda/matchers/integrations/libraries/active_model.rb +1 -1
  67. data/lib/shoulda/matchers/integrations/libraries/active_record.rb +1 -1
  68. data/lib/shoulda/matchers/integrations/libraries/rails.rb +2 -1
  69. data/lib/shoulda/matchers/integrations/libraries/routing.rb +27 -0
  70. data/lib/shoulda/matchers/integrations/test_frameworks/active_support_test_case.rb +1 -1
  71. data/lib/shoulda/matchers/integrations/test_frameworks/minitest_4.rb +1 -1
  72. data/lib/shoulda/matchers/integrations/test_frameworks/minitest_5.rb +1 -1
  73. data/lib/shoulda/matchers/integrations/test_frameworks/missing_test_framework.rb +1 -1
  74. data/lib/shoulda/matchers/integrations/test_frameworks/rspec.rb +2 -2
  75. data/lib/shoulda/matchers/integrations/test_frameworks/test_unit.rb +1 -1
  76. data/lib/shoulda/matchers/routing.rb +10 -0
  77. data/lib/shoulda/matchers/version.rb +1 -1
  78. data/script/SUPPORTED_VERSIONS +1 -1
  79. data/spec/acceptance/independent_matchers_spec.rb +103 -42
  80. data/spec/doublespeak_spec_helper.rb +5 -1
  81. data/spec/support/acceptance/adds_shoulda_matchers_to_project.rb +34 -11
  82. data/spec/support/acceptance/helpers/rspec_helpers.rb +9 -13
  83. data/spec/support/acceptance/helpers/step_helpers.rb +13 -0
  84. data/spec/support/acceptance/matchers/have_output.rb +1 -1
  85. data/spec/support/acceptance/matchers/indicate_number_of_tests_was_run_matcher.rb +1 -1
  86. data/spec/support/tests/command_runner.rb +5 -1
  87. data/spec/support/unit/helpers/active_record_versions.rb +0 -4
  88. data/spec/support/unit/shared_examples/set_session_or_flash.rb +8 -3
  89. data/spec/unit/shoulda/matchers/action_controller/permit_matcher_spec.rb +198 -39
  90. data/spec/unit/shoulda/matchers/action_controller/route_matcher_spec.rb +269 -102
  91. data/spec/unit/shoulda/matchers/action_controller/set_flash_matcher_spec.rb +24 -0
  92. data/spec/unit/shoulda/matchers/active_model/allow_value_matcher_spec.rb +118 -101
  93. data/spec/unit/shoulda/matchers/active_model/disallow_value_matcher_spec.rb +0 -82
  94. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/comparison_matcher_spec.rb +148 -121
  95. data/spec/unit/shoulda/matchers/active_model/validate_acceptance_of_matcher_spec.rb +20 -8
  96. data/spec/unit/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +64 -183
  97. data/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +14 -0
  98. data/spec/unit/shoulda/matchers/doublespeak/double_collection_spec.rb +60 -0
  99. data/spec/unit/shoulda/matchers/doublespeak/double_spec.rb +23 -7
  100. data/spec/unit/shoulda/matchers/routing/route_matcher_spec.rb +242 -0
  101. data/spec/unit_spec_helper.rb +4 -0
  102. data/tasks/documentation.rb +35 -0
  103. metadata +9 -8
  104. data/Guardfile +0 -5
  105. data/cucumber.yml +0 -1
  106. data/lib/shoulda/matchers/active_model/validator_with_captured_range_error.rb +0 -12
@@ -18,7 +18,7 @@ module AcceptanceTests
18
18
  "Expected command to have output, but did not.\n\n" +
19
19
  "Command: #{runner.formatted_command}\n\n" +
20
20
  "Expected output:\n" +
21
- output + "\n\n" +
21
+ output.inspect + "\n\n" +
22
22
  "Actual output:\n" +
23
23
  runner.output
24
24
  end
@@ -40,7 +40,7 @@ module AcceptanceTests
40
40
  private
41
41
 
42
42
  def expected_output
43
- /#{number} (tests|runs), #{number} assertions, 0 failures, 0 errors(, 0 skips)?/
43
+ /#{number} (?:tests?|runs?|examples?)(?:, #{number} assertions)?, 0 failures(?:, 0 errors(?:, 0 skips)?)?/
44
44
  end
45
45
 
46
46
  def actual_output
@@ -117,7 +117,11 @@ Output:
117
117
  end
118
118
 
119
119
  def has_output?(expected_output)
120
- output.include?(expected_output)
120
+ if expected_output.is_a?(Regexp)
121
+ output =~ expected_output
122
+ else
123
+ output.include?(expected_output)
124
+ end
121
125
  end
122
126
 
123
127
  protected
@@ -9,10 +9,6 @@ module UnitTests
9
9
  Tests::Version.new(ActiveRecord::VERSION::STRING)
10
10
  end
11
11
 
12
- def active_record_can_raise_range_error?
13
- active_record_version >= 4.2
14
- end
15
-
16
12
  def active_record_supports_enum?
17
13
  defined?(::ActiveRecord::Enum)
18
14
  end
@@ -56,9 +56,14 @@ shared_examples_for 'set session or flash matcher' do
56
56
 
57
57
  context 'in the positive' do
58
58
  context 'if the given key is present in the store' do
59
- it 'accepts' do
60
- controller = controller_with_store('the key' => 'any value')
61
- expect(controller).to set_store['the key']
59
+ it 'accepts the param as a string' do
60
+ controller = controller_with_store('the_key' => 'any value')
61
+ expect(controller).to set_store['the_key']
62
+ end
63
+
64
+ it 'accepts the param as a symbol' do
65
+ controller = controller_with_store('the_key' => 'any value')
66
+ expect(controller).to set_store[:the_key]
62
67
  end
63
68
  end
64
69
 
@@ -7,7 +7,7 @@ describe Shoulda::Matchers::ActionController::PermitMatcher, type: :controller d
7
7
  params_with_conditional_require(ctrl.params).permit(:name, :age)
8
8
  end
9
9
 
10
- expect(controller).to permit_with_conditional_params(
10
+ expect(controller).to permit_with_conditional_slice_of_params(
11
11
  permit(:name).for(:create)
12
12
  )
13
13
  end
@@ -17,7 +17,7 @@ describe Shoulda::Matchers::ActionController::PermitMatcher, type: :controller d
17
17
  params_with_conditional_require(ctrl.params).permit(:name, :age)
18
18
  end
19
19
 
20
- expect(controller).to permit_with_conditional_params(
20
+ expect(controller).to permit_with_conditional_slice_of_params(
21
21
  permit(:name, :age).for(:create)
22
22
  )
23
23
  end
@@ -27,7 +27,7 @@ describe Shoulda::Matchers::ActionController::PermitMatcher, type: :controller d
27
27
  params_with_conditional_require(ctrl.params).permit(:name)
28
28
  end
29
29
 
30
- expect(controller).not_to permit_with_conditional_params(
30
+ expect(controller).not_to permit_with_conditional_slice_of_params(
31
31
  permit(:name, :admin).for(:create)
32
32
  )
33
33
  end
@@ -35,22 +35,17 @@ describe Shoulda::Matchers::ActionController::PermitMatcher, type: :controller d
35
35
  it 'rejects when #permit has not been called' do
36
36
  define_controller_with_strong_parameters(action: :create)
37
37
 
38
- expect(controller).not_to permit_with_conditional_params(
38
+ expect(controller).not_to permit_with_conditional_slice_of_params(
39
39
  permit(:name).for(:create)
40
40
  )
41
41
  end
42
42
 
43
- it 'tracks multiple calls to #permit' do
43
+ it 'tracks multiple calls to #permit for different subparameters' do
44
44
  sets_of_attributes = [
45
45
  [:eta, :diner_id],
46
46
  [:phone_number, :address_1, :address_2, :city, :state, :zip]
47
47
  ]
48
48
 
49
- params = {
50
- order: { some: 'value' },
51
- diner: { some: 'value' }
52
- }
53
-
54
49
  define_controller_with_strong_parameters(action: :create) do |ctrl|
55
50
  params_with_conditional_require(ctrl.params, :order).
56
51
  permit(sets_of_attributes[0])
@@ -59,14 +54,16 @@ describe Shoulda::Matchers::ActionController::PermitMatcher, type: :controller d
59
54
  permit(sets_of_attributes[1])
60
55
  end
61
56
 
62
- expect(controller).to permit_with_conditional_params(
57
+ expect(controller).to permit_with_conditional_slice_of_params(
63
58
  permit(*sets_of_attributes[0]).for(:create),
64
- params
59
+ all_params: [:order, :diner],
60
+ selected_param: :order
65
61
  )
66
62
 
67
- expect(controller).to permit_with_conditional_params(
63
+ expect(controller).to permit_with_conditional_slice_of_params(
68
64
  permit(*sets_of_attributes[1]).for(:create),
69
- params
65
+ all_params: [:order, :diner],
66
+ selected_param: :diner
70
67
  )
71
68
  end
72
69
  end
@@ -91,7 +88,7 @@ describe Shoulda::Matchers::ActionController::PermitMatcher, type: :controller d
91
88
 
92
89
  context 'when operating on the entire params hash' do
93
90
  include_context 'basic tests' do
94
- def permit_with_conditional_params(permit, _params = {})
91
+ def permit_with_conditional_slice_of_params(permit, options = {})
95
92
  permit
96
93
  end
97
94
 
@@ -103,9 +100,16 @@ describe Shoulda::Matchers::ActionController::PermitMatcher, type: :controller d
103
100
 
104
101
  context 'when operating on a slice of the params hash' do
105
102
  include_context 'basic tests' do
106
- def permit_with_conditional_params(permit, params = nil)
107
- params ||= { user: { some: 'value' } }
108
- permit.add_params(params)
103
+ def permit_with_conditional_slice_of_params(
104
+ permit,
105
+ all_params: [:user],
106
+ selected_param: :user
107
+ )
108
+ params = all_params.reduce({}) do |hash, param|
109
+ hash.merge(param => { any: 'value' })
110
+ end
111
+
112
+ permit.add_params(params).on(selected_param)
109
113
  end
110
114
 
111
115
  def params_with_conditional_require(params, *filters)
@@ -127,6 +131,28 @@ describe Shoulda::Matchers::ActionController::PermitMatcher, type: :controller d
127
131
  for(:create, params: { order: { some: 'value' } }).
128
132
  on(:something_else)
129
133
  end
134
+
135
+ it 'tracks multiple calls to #permit for the same subparameter' do
136
+ define_controller_with_strong_parameters(action: :create) do
137
+ params.require(:foo).permit(:bar)
138
+ params.require(:foo).permit(:baz)
139
+ end
140
+
141
+ params = {
142
+ foo: {
143
+ bar: 'some value',
144
+ baz: 'some value'
145
+ }
146
+ }
147
+ expect(controller).
148
+ to permit(:bar).
149
+ on(:foo).
150
+ for(:create, params: params)
151
+ expect(controller).
152
+ to permit(:baz).
153
+ on(:foo).
154
+ for(:create, params: params)
155
+ end
130
156
  end
131
157
 
132
158
  it 'can be used more than once in the same test' do
@@ -138,7 +164,7 @@ describe Shoulda::Matchers::ActionController::PermitMatcher, type: :controller d
138
164
  expect(controller).not_to permit(:admin).for(:create)
139
165
  end
140
166
 
141
- it 'allows extra parameters to be passed to the action if it requires them' do
167
+ it 'allows extra parameters to be provided if the route requires them' do
142
168
  options = {
143
169
  controller_name: 'Posts',
144
170
  action: :show,
@@ -259,7 +285,7 @@ describe Shoulda::Matchers::ActionController::PermitMatcher, type: :controller d
259
285
 
260
286
  matcher = described_class.new([:name, :age, :height]).for(:create)
261
287
  expect(matcher.description).to eq(
262
- '(on POST #create) restrict parameters to :name, :age, and :height'
288
+ '(for POST #create) restrict parameters to :name, :age, and :height'
263
289
  )
264
290
  end
265
291
 
@@ -275,50 +301,183 @@ describe Shoulda::Matchers::ActionController::PermitMatcher, type: :controller d
275
301
  new([:name]).
276
302
  for(:some_action, verb: :put)
277
303
  expect(matcher.description).to eq(
278
- '(on PUT #some_action) restrict parameters to :name'
304
+ '(for PUT #some_action) restrict parameters to :name'
279
305
  )
280
306
  end
281
307
  end
282
308
  end
283
309
 
284
310
  describe 'positive failure message' do
285
- it 'includes all missing attributes' do
286
- define_controller_with_strong_parameters(action: :create) do
287
- params.permit(:name, :age)
311
+ context 'when no parameters were permitted' do
312
+ it 'returns the correct message' do
313
+ define_controller_with_strong_parameters(action: :create)
314
+
315
+ assertion = lambda do
316
+ expect(@controller).
317
+ to permit(:name, :age, :city, :country).
318
+ for(:create)
319
+ end
320
+
321
+ message =
322
+ 'Expected POST #create to restrict parameters to ' +
323
+ ":name, :age, :city, and :country,\n" +
324
+ 'but it did not restrict any parameters.'
325
+
326
+ expect(&assertion).to fail_with_message(message)
288
327
  end
328
+ end
289
329
 
290
- assertion = lambda do
291
- expect(@controller).
292
- to permit(:name, :age, :city, :country).
293
- for(:create)
330
+ context 'when some, but not all, parameters were permitted' do
331
+ it 'returns the correct message, including missing attributes' do
332
+ define_controller_with_strong_parameters(action: :create) do
333
+ params.permit(:name, :age)
334
+ end
335
+
336
+ assertion = lambda do
337
+ expect(@controller).
338
+ to permit(:name, :age, :city, :country).
339
+ for(:create)
340
+ end
341
+
342
+ message =
343
+ 'Expected POST #create to restrict parameters to ' +
344
+ ":name, :age, :city, and :country,\n" +
345
+ 'but the restricted parameters were :name and :age instead.'
346
+
347
+ expect(&assertion).to fail_with_message(message)
294
348
  end
349
+ end
295
350
 
296
- message =
297
- "Expected POST #create to restrict parameters to " +
298
- ":name, :age, :city, and :country,\n" +
299
- "but the restricted parameters were :name and :age instead."
351
+ context 'qualified with #on' do
352
+ context 'when the subparameter was never required' do
353
+ it 'returns the correct message' do
354
+ define_controller_with_strong_parameters(action: :create) do
355
+ params.permit(:name, :age)
356
+ end
300
357
 
301
- expect(&assertion).to fail_with_message(message)
358
+ assertion = lambda do
359
+ expect(@controller).
360
+ to permit(:name, :age, :city, :country).
361
+ for(:create).
362
+ on(:person)
363
+ end
364
+
365
+ message =
366
+ 'Expected POST #create to restrict parameters on :person to ' +
367
+ ":name, :age, :city, and :country,\n" +
368
+ 'but it did not restrict any parameters.'
369
+
370
+ expect(&assertion).to fail_with_message(message)
371
+ end
372
+ end
373
+
374
+ context 'when the subparameter was required' do
375
+ context 'but no parameters were permitted' do
376
+ it 'returns the correct message' do
377
+ define_controller_with_strong_parameters(action: :create) do
378
+ params.require(:person)
379
+ end
380
+
381
+ assertion = lambda do
382
+ params = {
383
+ person: {
384
+ name: 'some name',
385
+ age: 'some age'
386
+ }
387
+ }
388
+ expect(@controller).
389
+ to permit(:name, :age, :city, :country).
390
+ for(:create, params: params).
391
+ on(:person)
392
+ end
393
+
394
+ message =
395
+ 'Expected POST #create to restrict parameters on :person to ' +
396
+ ":name, :age, :city, and :country,\n" +
397
+ 'but it did not restrict any parameters.'
398
+
399
+ expect(&assertion).to fail_with_message(message)
400
+ end
401
+ end
402
+
403
+ context 'but some, but not all, parameters were permitted' do
404
+ it 'returns the correct message' do
405
+ define_controller_with_strong_parameters(action: :create) do
406
+ params.require(:person).permit(:name, :age)
407
+ end
408
+
409
+ assertion = lambda do
410
+ params = {
411
+ person: {
412
+ name: 'some name',
413
+ age: 'some age'
414
+ }
415
+ }
416
+ expect(@controller).
417
+ to permit(:name, :age, :city, :country).
418
+ for(:create, params: params).
419
+ on(:person)
420
+ end
421
+
422
+ message =
423
+ 'Expected POST #create to restrict parameters on :person to ' +
424
+ ":name, :age, :city, and :country,\n" +
425
+ 'but the restricted parameters were :name and :age instead.'
426
+
427
+ expect(&assertion).to fail_with_message(message)
428
+ end
429
+ end
430
+ end
302
431
  end
303
432
  end
304
433
 
305
434
  describe 'negative failure message' do
306
- it 'includes all attributes that should not have been permitted but were' do
435
+ it 'returns the correct message' do
307
436
  define_controller_with_strong_parameters(action: :create) do
308
- params.permit(:name, :age)
437
+ params.permit(:name, :age, :city, :country)
309
438
  end
310
439
 
311
440
  assertion = lambda do
312
- expect(controller).not_to permit(:name, :age).for(:create)
441
+ expect(@controller).
442
+ not_to permit(:name, :age, :city, :country).
443
+ for(:create)
313
444
  end
314
445
 
315
446
  message =
316
- "Expected POST #create not to restrict parameters to " +
317
- ":name and :age,\n" +
318
- "but it did."
447
+ 'Expected POST #create not to restrict parameters to ' +
448
+ ":name, :age, :city, and :country,\n" +
449
+ 'but it did.'
319
450
 
320
451
  expect(&assertion).to fail_with_message(message)
321
452
  end
453
+
454
+ context 'qualified with #on' do
455
+ it 'returns the correct message' do
456
+ define_controller_with_strong_parameters(action: :create) do
457
+ params.require(:person).permit(:name, :age)
458
+ end
459
+
460
+ assertion = lambda do
461
+ params = {
462
+ person: {
463
+ name: 'some name',
464
+ age: 'some age'
465
+ }
466
+ }
467
+ expect(@controller).
468
+ not_to permit(:name, :age).
469
+ for(:create, params: params).
470
+ on(:person)
471
+ end
472
+
473
+ message =
474
+ 'Expected POST #create not to restrict parameters on :person to ' +
475
+ ":name and :age,\n" +
476
+ 'but it did.'
477
+
478
+ expect(&assertion).to fail_with_message(message)
479
+ end
480
+ end
322
481
  end
323
482
 
324
483
  describe '#for' do
@@ -1,163 +1,330 @@
1
1
  require 'unit_spec_helper'
2
2
 
3
3
  describe 'Shoulda::Matchers::ActionController::RouteMatcher', type: :controller do
4
- shared_examples_for 'a controller with a defined route' do
5
- context 'when controller and action are specified as explicit options' do
4
+ shared_examples_for 'tests involving expected route parts' do |args|
5
+ include_controller_in_expected_route_options =
6
+ args.fetch(:include_controller_in_expected_route_options)
7
+
8
+ context 'when all parts of the expected route match an existing route' do
6
9
  it 'accepts' do
7
- expect(controller_with_defined_routes).
8
- to route(:get, "/#{controller_path}").
9
- to(action: 'index')
10
+ define_route :get, '/', action: 'index'
11
+
12
+ assert_accepts add_target_to(
13
+ route(:get, '/'),
14
+ build_expected_route_options(
15
+ include_controller_in_expected_route_options,
16
+ action: 'index'
17
+ )
18
+ )
10
19
  end
11
20
 
12
- it 'accepts a symbol controller' do
13
- expect(controller_with_defined_routes).
14
- to route(:get, "/#{controller_path}").
15
- to(controller: controller_path.to_sym, action: 'index')
21
+ if include_controller_in_expected_route_options
22
+ context 'and the expected controller is specified as a symbol' do
23
+ it 'accepts' do
24
+ define_route :get, '/', action: 'index'
25
+
26
+ assert_accepts add_target_to(
27
+ route(:get, '/'),
28
+ build_expected_route_options(
29
+ include_controller_in_expected_route_options,
30
+ action: 'index'
31
+ )
32
+ )
33
+ end
34
+ end
16
35
  end
17
36
 
18
- it 'accepts a symbol action' do
19
- expect(controller_with_defined_routes).
20
- to route(:get, "/#{controller_path}").
21
- to(action: :index)
37
+ context 'and the expected action is specified as a symbol' do
38
+ it 'accepts' do
39
+ define_route :get, '/', action: 'index'
40
+
41
+ assert_accepts add_target_to(
42
+ route(:get, '/'),
43
+ build_expected_route_options(
44
+ include_controller_in_expected_route_options,
45
+ action: :index
46
+ )
47
+ )
48
+ end
22
49
  end
50
+ end
23
51
 
24
- it 'rejects an undefined route' do
25
- expect(controller_with_defined_routes).
26
- not_to route(:get, '/non_existent_route').
27
- to(action: 'non_existent')
52
+ context 'when no parts of the expected route match an existing route' do
53
+ it 'rejects' do
54
+ assert_rejects add_target_to(
55
+ route(:get, '/non_existent_route'),
56
+ controller: 'no_controller',
57
+ action: 'no_action'
58
+ )
28
59
  end
60
+ end
29
61
 
30
- it 'rejects a route for another controller' do
31
- define_controller_with_defined_routes
32
- other_controller = define_controller('Other').new
33
- expect(other_controller).
34
- not_to route(:get, "/#{controller_path}").
35
- to(action: 'index')
62
+ context 'when all parts of the expected route but the method match an existing route' do
63
+ it 'rejects' do
64
+ define_route :post, '/', action: 'index'
65
+
66
+ assert_rejects add_target_to(
67
+ route(:get, '/'),
68
+ build_expected_route_options(
69
+ include_controller_in_expected_route_options,
70
+ action: 'index'
71
+ )
72
+ )
36
73
  end
74
+ end
37
75
 
38
- context 'when route has parameters' do
39
- it 'accepts a non-string parameter' do
40
- expect(controller_with_defined_routes).
41
- to route(:get, "/#{controller_path}/1").
42
- to(action: 'show', id: 1)
43
- end
76
+ context 'when all parts of the expected route but the path match an existing route' do
77
+ it 'rejects' do
78
+ define_route :get, '/', action: 'index'
79
+
80
+ assert_rejects add_target_to(
81
+ route(:get, '/different_path'),
82
+ build_expected_route_options(
83
+ include_controller_in_expected_route_options,
84
+ action: 'index'
85
+ )
86
+ )
87
+ end
88
+ end
44
89
 
45
- it 'rejects a route for different parameters' do
46
- expect(controller_with_defined_routes).
47
- not_to route(:get, "/#{controller_path}/1").
48
- to(action: 'show', some: 'other', params: 'here')
90
+ if include_controller_in_expected_route_options
91
+ context 'when all parts of the expected route but the controller match an existing route' do
92
+ it 'rejects' do
93
+ define_route :get, '/', controller: 'another_controller', action: 'index'
94
+
95
+ assert_rejects add_target_to(
96
+ route(:get, '/'),
97
+ build_expected_route_options(
98
+ include_controller_in_expected_route_options,
99
+ action: 'index'
100
+ )
101
+ )
49
102
  end
50
103
  end
104
+ end
51
105
 
52
- context 'when route has a default format' do
106
+ context 'when all parts of the expected route but the action match an existing route' do
107
+ it 'rejects' do
108
+ define_route :get, '/', action: 'index'
109
+
110
+ assert_rejects add_target_to(
111
+ route(:get, '/'),
112
+ build_expected_route_options(
113
+ include_controller_in_expected_route_options,
114
+ action: 'another_action'
115
+ )
116
+ )
117
+ end
118
+ end
119
+ end
120
+
121
+ shared_examples_for 'tests involving params' do
122
+ context 'when the actual route has a param' do
123
+ context 'and the expected params include that param' do
53
124
  it 'accepts' do
54
- expect(controller_with_defined_routes).
55
- to route(:post, "/#{controller_path}").
56
- to(action: 'create', format: 'json')
125
+ define_route :get, "/#{controller_name}/:id", action: 'show'
126
+
127
+ assert_accepts add_target_to(
128
+ route(:get, "/#{controller_name}/1"),
129
+ controller: controller_name,
130
+ action: 'show',
131
+ id: '1'
132
+ )
57
133
  end
58
134
 
59
- it 'accepts when format is specified as a symbol' do
60
- expect(controller_with_defined_routes).
61
- to route(:post, "/#{controller_path}").
62
- to(action: 'create', format: :json)
135
+ context 'but its value was not specified as a string' do
136
+ it 'accepts, treating it as a string' do
137
+ define_route :get, "/#{controller_name}/:id", action: 'show'
138
+
139
+ assert_accepts add_target_to(
140
+ route(:get, "/#{controller_name}/1"),
141
+ controller: controller_name,
142
+ action: 'show',
143
+ id: 1
144
+ )
145
+ end
63
146
  end
147
+ end
64
148
 
65
- it 'rejects when format is unspecified' do
66
- expect(controller_with_defined_routes).
67
- not_to route(:post, "/#{controller_path}").
68
- to(action: 'create')
149
+ context 'and the expected params do not match the actual params' do
150
+ it 'rejects' do
151
+ define_route :get, "/#{controller_name}/:id", action: 'show'
152
+
153
+ params = {
154
+ controller: controller_name,
155
+ action: 'show',
156
+ some: 'other',
157
+ params: 'here'
158
+ }
159
+ assert_rejects add_target_to(
160
+ route(:get, "/#{controller_name}/:id"),
161
+ params
162
+ )
69
163
  end
70
164
  end
71
165
  end
72
166
 
73
- context 'when controller and action are specified as a joined string' do
74
- it 'accepts' do
75
- expect(controller_with_defined_routes).
76
- to route(:get, "/#{controller_path}").
77
- to("#{controller_path}#index")
78
- end
167
+ context 'when the actual route has a default param whose value is a symbol' do
168
+ context 'and the expected params include a value for it' do
169
+ context 'as a symbol' do
170
+ it 'accepts' do
171
+ define_route :post, "/#{controller_name}/(.:format)",
172
+ action: 'create',
173
+ defaults: { format: :json }
174
+
175
+ assert_accepts add_target_to(
176
+ route(:post, "/#{controller_name}"),
177
+ controller: controller_name,
178
+ action: 'create',
179
+ format: :json
180
+ )
181
+ end
182
+ end
183
+
184
+ context 'as a string' do
185
+ it 'accepts' do
186
+ define_route :post, "/#{controller_name}/(.:format)",
187
+ action: 'create',
188
+ defaults: { format: :json }
79
189
 
80
- context 'when route has parameters' do
81
- it 'accepts a non-string parameter' do
82
- expect(controller_with_defined_routes).
83
- to route(:get, "/#{controller_path}/1").
84
- to("#{controller_path}#show", id: 1)
190
+ assert_accepts add_target_to(
191
+ route(:post, "/#{controller_name}"),
192
+ controller: controller_name,
193
+ action: 'create',
194
+ format: 'json'
195
+ )
196
+ end
85
197
  end
86
198
  end
199
+ end
87
200
 
88
- context 'when route has the format' do
201
+ context 'when the existing route has a glob segment' do
202
+ context 'and a param is given which represents the segment' do
89
203
  it 'accepts' do
90
- expect(controller_with_defined_routes).
91
- to route(:post, "/#{controller_path}").
92
- to("#{controller_path}#create", format: 'json')
204
+ define_route :get, "/#{controller_name}/*id", action: 'whatever'
205
+
206
+ assert_accepts add_target_to(
207
+ route(:get, "/#{controller_name}/foo/bar"),
208
+ controller: controller_name,
209
+ action: 'whatever',
210
+ id: 'foo/bar'
211
+ )
93
212
  end
213
+ end
214
+
215
+ context 'and no param is given which represents the segment' do
216
+ it 'rejects' do
217
+ define_route :get, "/#{controller_name}/*id", action: 'whatever'
94
218
 
95
- it 'rejects when format is unspecified' do
96
- expect(controller_with_defined_routes).
97
- not_to route(:post, "/#{controller_path}").
98
- to(action: 'create')
219
+ assert_rejects add_target_to(
220
+ route(:get, "/#{controller_name}"),
221
+ controller: controller_name,
222
+ action: 'whatever'
223
+ )
99
224
  end
100
225
  end
101
226
  end
227
+ end
102
228
 
103
- def controller_with_defined_routes
104
- @_controller_with_defined_routes ||= begin
105
- controller_class = define_controller(controller_name)
106
- _controller_path = controller_path
107
-
108
- setup_rails_controller_test(controller_class)
229
+ shared_examples_for 'core tests' do
230
+ context 'given a controller and action specified as individual options' do
231
+ include_examples 'tests involving expected route parts',
232
+ include_controller_in_expected_route_options: true
109
233
 
110
- define_routes do
111
- get "/#{_controller_path}", to: "#{_controller_path}#index"
112
- get "/#{_controller_path}/:id", to: "#{_controller_path}#show"
113
- post "/#{_controller_path}",
114
- to: "#{_controller_path}#create",
115
- defaults: { format: :json }
116
- end
234
+ include_examples 'tests involving params'
117
235
 
118
- controller
236
+ def add_target_to(route_matcher, params)
237
+ route_matcher.to(params)
119
238
  end
120
239
  end
121
240
 
122
- def controller_path
123
- controller_name.sub(/Controller$/, '').underscore
241
+ context 'given a controller and action joined together in a string' do
242
+ include_examples 'tests involving expected route parts',
243
+ include_controller_in_expected_route_options: true
244
+
245
+ include_examples 'tests involving params'
246
+
247
+ def add_target_to(route_matcher, args)
248
+ controller = args.fetch(:controller)
249
+ action = args.fetch(:action)
250
+ route_matcher.to("#{controller}##{action}", args)
251
+ end
124
252
  end
125
253
 
126
- alias_method :define_controller_with_defined_routes,
127
- :controller_with_defined_routes
128
- end
254
+ context 'given just an action' do
255
+ include_examples 'tests involving expected route parts',
256
+ include_controller_in_expected_route_options: false
129
257
 
130
- context 'given a controller with a defined glob url' do
131
- it 'accepts glob route' do
132
- controller_class = define_controller('Examples')
133
- setup_rails_controller_test(controller_class)
258
+ include_examples 'tests involving params'
134
259
 
135
- define_routes do
136
- get '/examples/*id', to: 'examples#example'
260
+ def add_target_to(route_matcher, params)
261
+ route_matcher.to(params)
137
262
  end
138
-
139
- expect(controller).to route(:get, '/examples/foo/bar').
140
- to(action: 'example', id: 'foo/bar')
141
263
  end
142
264
  end
143
265
 
266
+ before do
267
+ setup_rails_controller_test(controller_class)
268
+ end
269
+
144
270
  context 'given a controller that is not namespaced' do
145
- it_behaves_like 'a controller with a defined route' do
146
- def controller_name
147
- 'ExamplesController'
148
- end
271
+ include_examples 'core tests'
272
+
273
+ def controller_class_name
274
+ 'ExamplesController'
149
275
  end
150
276
  end
151
277
 
152
278
  context 'given a controller that is namespaced' do
153
- it_behaves_like 'a controller with a defined route' do
154
- before do
155
- define_module('Admin')
156
- end
279
+ def define_controller_under_test
280
+ define_module('Admin')
281
+ super
282
+ end
283
+
284
+ include_examples 'core tests'
157
285
 
158
- def controller_name
159
- 'Admin::ExamplesController'
286
+ def controller_class_name
287
+ 'Admin::ExamplesController'
288
+ end
289
+ end
290
+
291
+ let(:controller_class) do
292
+ define_controller_under_test
293
+ end
294
+
295
+ def define_controller_under_test
296
+ define_controller(controller_class_name)
297
+ end
298
+
299
+ def controller_name
300
+ controller_class_name.sub(/Controller$/, '').underscore
301
+ end
302
+
303
+ def define_route(method, path, args)
304
+ action = args.fetch(:action)
305
+ controller = args.fetch(:controller) { controller_name }
306
+ define_routes do
307
+ public_send(
308
+ method,
309
+ path,
310
+ args.merge(controller: controller, action: action)
311
+ )
312
+ end
313
+ end
314
+
315
+ def build_expected_route_options(include_controller_in_expected_route_options, default_options)
316
+ default_options.dup.tap do |options|
317
+ if include_controller_in_expected_route_options
318
+ options[:controller] = controller_name
160
319
  end
161
320
  end
162
321
  end
322
+
323
+ def assert_accepts(matcher)
324
+ expect(controller).to matcher
325
+ end
326
+
327
+ def assert_rejects(matcher)
328
+ expect(controller).not_to matcher
329
+ end
163
330
  end