shoulda-matchers 2.8.0 → 3.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (171) hide show
  1. checksums.yaml +4 -4
  2. data/.hound_config/ruby.yml +7 -0
  3. data/.travis.yml +11 -54
  4. data/Appraisals +45 -100
  5. data/CONTRIBUTING.md +51 -7
  6. data/Gemfile +7 -19
  7. data/Gemfile.lock +60 -134
  8. data/Guardfile +5 -0
  9. data/NEWS.md +203 -0
  10. data/README.md +95 -50
  11. data/Rakefile +1 -0
  12. data/doc_config/yard/templates/default/layout/html/setup.rb +1 -1
  13. data/gemfiles/4.0.0.gemfile +10 -7
  14. data/gemfiles/4.0.0.gemfile.lock +103 -79
  15. data/gemfiles/4.0.1.gemfile +10 -7
  16. data/gemfiles/4.0.1.gemfile.lock +109 -83
  17. data/gemfiles/4.1.gemfile +10 -7
  18. data/gemfiles/4.1.gemfile.lock +109 -85
  19. data/gemfiles/4.2.gemfile +10 -9
  20. data/gemfiles/4.2.gemfile.lock +86 -78
  21. data/lib/shoulda/matchers.rb +13 -18
  22. data/lib/shoulda/matchers/action_controller.rb +4 -1
  23. data/lib/shoulda/matchers/action_controller/flash_store.rb +95 -0
  24. data/lib/shoulda/matchers/action_controller/{strong_parameters_matcher.rb → permit_matcher.rb} +147 -30
  25. data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +1 -1
  26. data/lib/shoulda/matchers/action_controller/render_template_matcher.rb +1 -1
  27. data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +1 -1
  28. data/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +1 -1
  29. data/lib/shoulda/matchers/action_controller/route_matcher.rb +5 -1
  30. data/lib/shoulda/matchers/action_controller/route_params.rb +15 -6
  31. data/lib/shoulda/matchers/action_controller/session_store.rb +34 -0
  32. data/lib/shoulda/matchers/action_controller/set_flash_matcher.rb +30 -136
  33. data/lib/shoulda/matchers/action_controller/set_session_matcher.rb +28 -109
  34. data/lib/shoulda/matchers/action_controller/set_session_or_flash_matcher.rb +103 -0
  35. data/lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb +1 -12
  36. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +79 -10
  37. data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +10 -0
  38. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +21 -0
  39. data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +24 -0
  40. data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +22 -5
  41. data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +29 -10
  42. data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +27 -10
  43. data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +27 -12
  44. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +56 -20
  45. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +3 -11
  46. data/lib/shoulda/matchers/active_model/validation_message_finder.rb +65 -0
  47. data/lib/shoulda/matchers/active_record/association_matcher.rb +40 -6
  48. data/lib/shoulda/matchers/active_record/association_matchers/join_table_matcher.rb +21 -7
  49. data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +11 -40
  50. data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +1 -1
  51. data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +2 -6
  52. data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +137 -22
  53. data/lib/shoulda/matchers/configuration.rb +20 -0
  54. data/lib/shoulda/matchers/doublespeak.rb +11 -1
  55. data/lib/shoulda/matchers/doublespeak/double.rb +29 -11
  56. data/lib/shoulda/matchers/doublespeak/double_collection.rb +4 -3
  57. data/lib/shoulda/matchers/doublespeak/method_call.rb +35 -0
  58. data/lib/shoulda/matchers/doublespeak/object_double.rb +7 -2
  59. data/lib/shoulda/matchers/doublespeak/proxy_implementation.rb +4 -3
  60. data/lib/shoulda/matchers/doublespeak/stub_implementation.rb +3 -3
  61. data/lib/shoulda/matchers/doublespeak/world.rb +21 -1
  62. data/lib/shoulda/matchers/integrations.rb +43 -0
  63. data/lib/shoulda/matchers/integrations/configuration.rb +68 -0
  64. data/lib/shoulda/matchers/integrations/configuration_error.rb +9 -0
  65. data/lib/shoulda/matchers/integrations/inclusion.rb +20 -0
  66. data/lib/shoulda/matchers/integrations/libraries.rb +15 -0
  67. data/lib/shoulda/matchers/integrations/libraries/action_controller.rb +31 -0
  68. data/lib/shoulda/matchers/integrations/libraries/active_model.rb +26 -0
  69. data/lib/shoulda/matchers/integrations/libraries/active_record.rb +26 -0
  70. data/lib/shoulda/matchers/integrations/libraries/missing_library.rb +19 -0
  71. data/lib/shoulda/matchers/integrations/libraries/rails.rb +30 -0
  72. data/lib/shoulda/matchers/integrations/rails.rb +12 -0
  73. data/lib/shoulda/matchers/integrations/registry.rb +28 -0
  74. data/lib/shoulda/matchers/integrations/test_frameworks.rb +16 -0
  75. data/lib/shoulda/matchers/integrations/test_frameworks/active_support_test_case.rb +37 -0
  76. data/lib/shoulda/matchers/integrations/test_frameworks/minitest_4.rb +36 -0
  77. data/lib/shoulda/matchers/integrations/test_frameworks/minitest_5.rb +37 -0
  78. data/lib/shoulda/matchers/integrations/test_frameworks/missing_test_framework.rb +40 -0
  79. data/lib/shoulda/matchers/integrations/test_frameworks/rspec.rb +29 -0
  80. data/lib/shoulda/matchers/integrations/test_frameworks/test_unit.rb +36 -0
  81. data/lib/shoulda/matchers/rails_shim.rb +0 -40
  82. data/lib/shoulda/matchers/version.rb +1 -1
  83. data/script/SUPPORTED_VERSIONS +1 -1
  84. data/script/update_gems_in_all_appraisals +14 -0
  85. data/shoulda-matchers.gemspec +2 -2
  86. data/spec/acceptance/active_model_integration_spec.rb +4 -1
  87. data/spec/acceptance/independent_matchers_spec.rb +6 -6
  88. data/spec/acceptance/multiple_libraries_integration_spec.rb +52 -0
  89. data/spec/acceptance/rails_integration_spec.rb +15 -5
  90. data/spec/acceptance_spec_helper.rb +8 -0
  91. data/spec/doublespeak_spec_helper.rb +14 -0
  92. data/spec/support/acceptance/adds_shoulda_matchers_to_project.rb +110 -0
  93. data/spec/support/acceptance/helpers.rb +2 -0
  94. data/spec/support/acceptance/helpers/base_helpers.rb +6 -1
  95. data/spec/support/acceptance/helpers/command_helpers.rb +6 -2
  96. data/spec/support/acceptance/helpers/minitest_helpers.rb +0 -8
  97. data/spec/support/acceptance/helpers/n_unit_helpers.rb +25 -0
  98. data/spec/support/acceptance/helpers/rspec_helpers.rb +2 -0
  99. data/spec/support/acceptance/helpers/step_helpers.rb +13 -19
  100. data/spec/support/acceptance/matchers/have_output.rb +1 -1
  101. data/spec/support/tests/bundle.rb +1 -1
  102. data/spec/support/tests/command_runner.rb +25 -13
  103. data/spec/support/tests/current_bundle.rb +47 -0
  104. data/spec/support/tests/database.rb +28 -0
  105. data/spec/support/tests/database_adapters/postgresql.rb +25 -0
  106. data/spec/support/tests/database_adapters/sqlite3.rb +26 -0
  107. data/spec/support/tests/database_configuration.rb +33 -0
  108. data/spec/support/tests/database_configuration_registry.rb +28 -0
  109. data/spec/support/tests/filesystem.rb +25 -2
  110. data/spec/support/unit/helpers/active_record_versions.rb +12 -0
  111. data/spec/support/unit/helpers/class_builder.rb +6 -2
  112. data/spec/support/unit/helpers/column_type_helpers.rb +26 -0
  113. data/spec/support/unit/helpers/controller_builder.rb +0 -28
  114. data/spec/support/unit/helpers/database_helpers.rb +18 -0
  115. data/spec/support/unit/helpers/model_builder.rb +38 -6
  116. data/spec/support/unit/helpers/rails_versions.rb +2 -2
  117. data/spec/support/unit/matchers/fail_with_message_including_matcher.rb +9 -8
  118. data/spec/support/unit/matchers/fail_with_message_matcher.rb +1 -1
  119. data/spec/support/unit/rails_application.rb +29 -13
  120. data/spec/support/unit/record_validating_confirmation_builder.rb +1 -2
  121. data/spec/support/unit/shared_examples/set_session_or_flash.rb +355 -0
  122. data/spec/unit/shoulda/matchers/action_controller/permit_matcher_spec.rb +433 -0
  123. data/spec/unit/shoulda/matchers/action_controller/render_with_layout_matcher_spec.rb +1 -5
  124. data/spec/unit/shoulda/matchers/action_controller/route_matcher_spec.rb +37 -0
  125. data/spec/unit/shoulda/matchers/action_controller/set_flash_matcher_spec.rb +23 -147
  126. data/spec/unit/shoulda/matchers/action_controller/set_session_matcher_spec.rb +8 -285
  127. data/spec/unit/shoulda/matchers/action_controller/set_session_or_flash_matcher_spec.rb +562 -0
  128. data/spec/unit/shoulda/matchers/active_model/allow_value_matcher_spec.rb +81 -14
  129. data/spec/unit/shoulda/matchers/active_model/disallow_value_matcher_spec.rb +16 -8
  130. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/comparison_matcher_spec.rb +101 -9
  131. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/even_number_matcher_spec.rb +39 -1
  132. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher_spec.rb +39 -1
  133. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher_spec.rb +39 -0
  134. data/spec/unit/shoulda/matchers/active_model/validate_exclusion_of_matcher_spec.rb +0 -17
  135. data/spec/unit/shoulda/matchers/active_model/validate_inclusion_of_matcher_spec.rb +0 -17
  136. data/spec/unit/shoulda/matchers/active_model/validate_length_of_matcher_spec.rb +0 -17
  137. data/spec/unit/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +838 -271
  138. data/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +0 -19
  139. data/spec/unit/shoulda/matchers/active_record/association_matcher_spec.rb +93 -0
  140. data/spec/unit/shoulda/matchers/active_record/association_matchers/model_reflection_spec.rb +3 -3
  141. data/spec/unit/shoulda/matchers/active_record/define_enum_for_matcher_spec.rb +25 -0
  142. data/spec/unit/shoulda/matchers/active_record/validate_uniqueness_of_matcher_spec.rb +905 -0
  143. data/spec/unit/shoulda/matchers/doublespeak/double_collection_spec.rb +17 -11
  144. data/spec/unit/shoulda/matchers/doublespeak/double_implementation_registry_spec.rb +1 -1
  145. data/spec/unit/shoulda/matchers/doublespeak/double_spec.rb +144 -43
  146. data/spec/unit/shoulda/matchers/doublespeak/object_double_spec.rb +1 -1
  147. data/spec/unit/shoulda/matchers/doublespeak/proxy_implementation_spec.rb +36 -11
  148. data/spec/unit/shoulda/matchers/doublespeak/stub_implementation_spec.rb +29 -16
  149. data/spec/unit/shoulda/matchers/doublespeak/world_spec.rb +8 -5
  150. data/spec/unit/shoulda/matchers/doublespeak_spec.rb +1 -1
  151. data/spec/unit_spec_helper.rb +15 -14
  152. data/spec/warnings_spy.rb +1 -1
  153. metadata +68 -29
  154. data/docs.watchr +0 -5
  155. data/gemfiles/3.0.gemfile +0 -26
  156. data/gemfiles/3.0.gemfile.lock +0 -173
  157. data/gemfiles/3.1.gemfile +0 -32
  158. data/gemfiles/3.1.gemfile.lock +0 -212
  159. data/gemfiles/3.1_1.9.2.gemfile +0 -32
  160. data/gemfiles/3.1_1.9.2.gemfile.lock +0 -212
  161. data/gemfiles/3.2.gemfile +0 -33
  162. data/gemfiles/3.2.gemfile.lock +0 -212
  163. data/gemfiles/3.2_1.9.2.gemfile +0 -31
  164. data/gemfiles/3.2_1.9.2.gemfile.lock +0 -207
  165. data/lib/shoulda/matchers/assertion_error.rb +0 -27
  166. data/lib/shoulda/matchers/doublespeak/structs.rb +0 -10
  167. data/lib/shoulda/matchers/integrations/nunit_test_case_detection.rb +0 -39
  168. data/lib/shoulda/matchers/integrations/rspec.rb +0 -19
  169. data/lib/shoulda/matchers/integrations/test_unit.rb +0 -34
  170. data/spec/unit/shoulda/matchers/action_controller/strong_parameters_matcher_spec.rb +0 -331
  171. data/spec/unit/shoulda/matchers/active_model/validate_uniqueness_of_matcher_spec.rb +0 -564
@@ -45,6 +45,44 @@ describe Shoulda::Matchers::ActiveModel::NumericalityMatchers::OddNumberMatcher
45
45
  end
46
46
  end
47
47
 
48
+ context 'asserting strict validation when validating strictly' do
49
+ it 'accepts' do
50
+ expect(validating_odd_number(strict: true)).to subject.strict
51
+ end
52
+ end
53
+
54
+ context 'asserting non-strict validation when validating strictly' do
55
+ it 'rejects' do
56
+ pending 'This needs to be fixed'
57
+ expect(validating_odd_number(strict: true)).not_to subject
58
+ end
59
+ end
60
+
61
+ context 'asserting strict validation when not validating strictly' do
62
+ it 'rejects' do
63
+ expect(validating_odd_number).not_to subject.strict
64
+ end
65
+ end
66
+
67
+ context 'qualified with on and validating with on' do
68
+ it 'accepts' do
69
+ expect(validating_odd_number(on: :customizable)).
70
+ to subject.on(:customizable)
71
+ end
72
+ end
73
+
74
+ context 'qualified with on but not validating with on' do
75
+ it 'accepts since the validation never considers a context' do
76
+ expect(validating_odd_number).to subject.on(:customizable)
77
+ end
78
+ end
79
+
80
+ context 'not qualified with on but validating with on' do
81
+ it 'rejects since the validation never runs' do
82
+ expect(validating_odd_number(on: :customizable)).
83
+ not_to subject
84
+ end
85
+ end
48
86
 
49
87
  def validating_odd_number(options = {})
50
88
  define_model :example, attr: :string do
@@ -56,4 +94,4 @@ describe Shoulda::Matchers::ActiveModel::NumericalityMatchers::OddNumberMatcher
56
94
  define_model(:example, attr: :string).new
57
95
  end
58
96
 
59
- end
97
+ end
@@ -45,6 +45,45 @@ describe Shoulda::Matchers::ActiveModel::NumericalityMatchers::OnlyIntegerMatche
45
45
  end
46
46
  end
47
47
 
48
+ context 'asserting strict validation when validating strictly' do
49
+ it 'accepts' do
50
+ expect(validating_only_integer(strict: true)).to subject.strict
51
+ end
52
+ end
53
+
54
+ context 'asserting non-strict validation when validating strictly' do
55
+ it 'rejects' do
56
+ pending 'This needs to be fixed'
57
+ expect(validating_only_integer(strict: true)).not_to subject
58
+ end
59
+ end
60
+
61
+ context 'asserting strict validation when not validating strictly' do
62
+ it 'rejects' do
63
+ expect(validating_only_integer).not_to subject.strict
64
+ end
65
+ end
66
+
67
+ context 'qualified with on and validating with on' do
68
+ it 'accepts' do
69
+ expect(validating_only_integer(on: :customizable)).
70
+ to subject.on(:customizable)
71
+ end
72
+ end
73
+
74
+ context 'qualified with on but not validating with on' do
75
+ it 'accepts since the validation never considers a context' do
76
+ expect(validating_only_integer).to subject.on(:customizable)
77
+ end
78
+ end
79
+
80
+ context 'not qualified with on but validating with on' do
81
+ it 'rejects since the validation never runs' do
82
+ expect(validating_only_integer(on: :customizable)).
83
+ not_to subject
84
+ end
85
+ end
86
+
48
87
  def validating_only_integer(options = {})
49
88
  define_model :example, attr: :string do
50
89
  validates_numericality_of :attr, { only_integer: true }.merge(options)
@@ -1,22 +1,5 @@
1
1
  require 'unit_spec_helper'
2
2
 
3
- describe Shoulda::Matchers::ActiveModel, type: :model do
4
- describe '#ensure_exclusion_of' do
5
- it 'is aliased to #validate_exclusion_of' do
6
- allow(matchers).to receive(:validate_exclusion_of)
7
-
8
- silence_warnings do
9
- matchers.ensure_exclusion_of(:attr)
10
- expect(matchers).to have_received(:validate_exclusion_of).with(:attr)
11
- end
12
- end
13
- end
14
-
15
- def matchers
16
- @_matchers ||= Object.new.extend(described_class)
17
- end
18
- end
19
-
20
3
  describe Shoulda::Matchers::ActiveModel::ValidateExclusionOfMatcher, type: :model do
21
4
  context 'an attribute which must be excluded from a range' do
22
5
  it 'accepts ensuring the correct range' do
@@ -1,22 +1,5 @@
1
1
  require 'unit_spec_helper'
2
2
 
3
- describe Shoulda::Matchers::ActiveModel, type: :model do
4
- describe '#ensure_inclusion_of' do
5
- it 'is aliased to #validate_inclusion_of' do
6
- allow(matchers).to receive(:validate_inclusion_of)
7
-
8
- silence_warnings do
9
- matchers.ensure_inclusion_of(:attr)
10
- expect(matchers).to have_received(:validate_inclusion_of)
11
- end
12
- end
13
- end
14
-
15
- def matchers
16
- @_matchers ||= Object.new.extend(described_class)
17
- end
18
- end
19
-
20
3
  describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :model do
21
4
  shared_context 'for a generic attribute' do
22
5
  def self.testing_values_of_option(option_name, &block)
@@ -1,22 +1,5 @@
1
1
  require 'unit_spec_helper'
2
2
 
3
- describe Shoulda::Matchers::ActiveModel, type: :model do
4
- describe '#ensure_length_of' do
5
- it 'is aliased to #validate_length_of' do
6
- allow(matchers).to receive(:validate_length_of)
7
-
8
- silence_warnings do
9
- matchers.ensure_length_of(:attr)
10
- expect(matchers).to have_received(:validate_length_of).with(:attr)
11
- end
12
- end
13
- end
14
-
15
- def matchers
16
- @_matchers ||= Object.new.extend(described_class)
17
- end
18
- end
19
-
20
3
  describe Shoulda::Matchers::ActiveModel::ValidateLengthOfMatcher, type: :model do
21
4
  context 'an attribute with a non-zero minimum length validation' do
22
5
  it 'accepts ensuring the correct minimum length' do
@@ -1,425 +1,992 @@
1
1
  require 'unit_spec_helper'
2
2
 
3
3
  describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :model do
4
- context 'with a model with a numericality validation' do
5
- it 'accepts' do
6
- expect(validating_numericality).to matcher
4
+ class << self
5
+ def all_qualifiers
6
+ [
7
+ {
8
+ category: :comparison,
9
+ name: :is_greater_than,
10
+ argument: 1,
11
+ validation_name: :greater_than,
12
+ validation_value: 1,
13
+ },
14
+ {
15
+ category: :comparison,
16
+ name: :is_greater_than_or_equal_to,
17
+ argument: 1,
18
+ validation_name: :greater_than_or_equal_to,
19
+ validation_value: 1,
20
+ },
21
+ {
22
+ category: :comparison,
23
+ name: :is_less_than,
24
+ argument: 1,
25
+ validation_name: :less_than,
26
+ validation_value: 1,
27
+ },
28
+ {
29
+ category: :comparison,
30
+ name: :is_less_than_or_equal_to,
31
+ argument: 1,
32
+ validation_name: :less_than_or_equal_to,
33
+ validation_value: 1,
34
+ },
35
+ {
36
+ category: :comparison,
37
+ name: :is_equal_to,
38
+ argument: 1,
39
+ validation_name: :equal_to,
40
+ validation_value: 1,
41
+ },
42
+ {
43
+ category: :cardinality,
44
+ name: :odd,
45
+ validation_name: :odd,
46
+ validation_value: true,
47
+ },
48
+ {
49
+ category: :cardinality,
50
+ name: :even,
51
+ validation_name: :even,
52
+ validation_value: true,
53
+ },
54
+ {
55
+ name: :only_integer,
56
+ validation_name: :only_integer,
57
+ validation_value: true,
58
+ },
59
+ {
60
+ name: :on,
61
+ argument: :customizable,
62
+ validation_name: :on,
63
+ validation_value: :customizable
64
+ }
65
+ ]
66
+ end
67
+
68
+ def qualifiers_under(category)
69
+ all_qualifiers.select do |qualifier|
70
+ qualifier[:category] == category
71
+ end
7
72
  end
8
73
 
9
- it 'does not override the default message with a blank' do
10
- expect(validating_numericality).to matcher.with_message(nil)
74
+ def mutually_exclusive_qualifiers
75
+ qualifiers_under(:cardinality) + qualifiers_under(:comparison)
11
76
  end
12
- end
13
77
 
14
- context 'with a model without a numericality validation' do
15
- it 'rejects' do
16
- expect(not_validating_numericality).not_to matcher
78
+ def non_mutually_exclusive_qualifiers
79
+ all_qualifiers - mutually_exclusive_qualifiers
17
80
  end
18
81
 
19
- it 'rejects with the ActiveRecord :not_a_number message' do
20
- the_matcher = matcher
21
- expect do
22
- expect(not_validating_numericality).to the_matcher
23
- end.to fail_with_message_including(
24
- 'Expected errors to include "is not a number"'
25
- )
82
+ def validations_by_qualifier
83
+ all_qualifiers.each_with_object({}) do |qualifier, hash|
84
+ hash[qualifier[:name]] = qualifier[:validation_name]
85
+ end
26
86
  end
27
87
 
28
- it 'rejects with the ActiveRecord :not_an_integer message' do
29
- the_matcher = matcher.only_integer
30
- expect do
31
- expect(not_validating_numericality).to the_matcher
32
- end.to fail_with_message_including(
33
- 'Expected errors to include "must be an integer"'
34
- )
88
+ def all_qualifier_combinations
89
+ combinations = []
90
+
91
+ ([nil] + mutually_exclusive_qualifiers).each do |mutually_exclusive_qualifier|
92
+ (0..non_mutually_exclusive_qualifiers.length).each do |n|
93
+ non_mutually_exclusive_qualifiers.combination(n) do |combination|
94
+ super_combination = (
95
+ [mutually_exclusive_qualifier] +
96
+ combination
97
+ )
98
+ super_combination.select!(&:present?)
99
+
100
+ if super_combination.any?
101
+ combinations << super_combination
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ combinations
35
108
  end
36
109
 
37
- it 'rejects with the ActiveRecord :odd message' do
38
- the_matcher = matcher.odd
39
- expect do
40
- expect(not_validating_numericality).to the_matcher
41
- end.to fail_with_message_including(
42
- 'Expected errors to include "must be odd"'
43
- )
110
+ def default_qualifier_arguments
111
+ all_qualifiers.each_with_object({}) do |qualifier, hash|
112
+ hash[qualifier[:name]] = qualifier[:argument]
113
+ end
44
114
  end
45
115
 
46
- it 'rejects with the ActiveRecord :even message' do
47
- the_matcher = matcher.even
48
- expect do
49
- expect(not_validating_numericality).to the_matcher
50
- end.to fail_with_message_including(
51
- 'Expected errors to include "must be even"'
52
- )
116
+ def default_validation_values
117
+ all_qualifiers.each_with_object({}) do |qualifier, hash|
118
+ hash[qualifier[:validation_name]] = qualifier[:validation_value]
119
+ end
53
120
  end
54
121
  end
55
122
 
56
- context 'with the allow_nil option' do
57
- it 'allows nil values for that attribute' do
58
- expect(validating_numericality(allow_nil: true)).to matcher.allow_nil
123
+ context 'qualified with nothing' do
124
+ context 'and validating numericality' do
125
+ it 'accepts' do
126
+ record = build_record_validating_numericality
127
+ expect(record).to validate_numericality
128
+ end
59
129
  end
60
130
 
61
- it 'rejects when the model does not allow nil' do
62
- the_matcher = matcher.allow_nil
63
- expect {
64
- expect(validating_numericality).to the_matcher
65
- }.to fail_with_message_including('Did not expect errors to include "is not a number"')
131
+ context 'and not validating anything' do
132
+ it 'rejects since it does not disallow non-numbers' do
133
+ record = build_record_validating_nothing
134
+ assertion = -> { expect(record).to validate_numericality }
135
+ expect(&assertion).to fail_with_message_including(
136
+ 'Expected errors to include "is not a number"'
137
+ )
138
+ end
66
139
  end
67
140
  end
68
141
 
69
- context 'with the only_integer option' do
70
- it 'allows integer values for that attribute' do
71
- expect(validating_numericality(only_integer: true)).to matcher.only_integer
142
+ context 'qualified with allow_nil' do
143
+ context 'and validating with allow_nil' do
144
+ it 'accepts' do
145
+ record = build_record_validating_numericality(allow_nil: true)
146
+ expect(record).to validate_numericality.allow_nil
147
+ end
72
148
  end
73
149
 
74
- it 'rejects when the model does not enforce integer values' do
75
- expect(validating_numericality).not_to matcher.only_integer
150
+ context 'and not validating with allow_nil' do
151
+ it 'rejects since it tries to treat nil as a number' do
152
+ record = build_record_validating_numericality
153
+ assertion = lambda do
154
+ expect(record).to validate_numericality.allow_nil
155
+ end
156
+ expect(&assertion).to fail_with_message_including(
157
+ %[Did not expect errors to include "is not a number" when #{attribute_name} is set to nil]
158
+ )
159
+ end
76
160
  end
161
+ end
77
162
 
78
- it 'rejects with the ActiveRecord :not_an_integer message' do
79
- the_matcher = matcher.only_integer
80
- expect do
81
- expect(validating_numericality).to the_matcher
82
- end.to fail_with_message_including(
83
- 'Expected errors to include "must be an integer"'
84
- )
163
+ context 'qualified with only_integer' do
164
+ context 'and validating with only_integer' do
165
+ it 'accepts' do
166
+ record = build_record_validating_numericality(only_integer: true)
167
+ expect(record).to validate_numericality.only_integer
168
+ end
85
169
  end
86
- end
87
170
 
88
- context 'with the odd option' do
89
- it 'allows odd number values for that attribute' do
90
- expect(validating_numericality(odd: true)).to matcher.odd
171
+ context 'and not validating with only_integer' do
172
+ it 'rejects since it does not disallow non-integers' do
173
+ record = build_record_validating_numericality
174
+ assertion = lambda do
175
+ expect(record).to validate_numericality.only_integer
176
+ end
177
+ expect(&assertion).to fail_with_message_including(
178
+ 'Expected errors to include "must be an integer"'
179
+ )
180
+ end
91
181
  end
182
+ end
92
183
 
93
- it 'rejects when the model does not enforce odd number values' do
94
- expect(validating_numericality).not_to matcher.odd
184
+ context 'qualified with odd' do
185
+ context 'and validating with odd' do
186
+ it 'accepts' do
187
+ record = build_record_validating_numericality(odd: true)
188
+ expect(record).to validate_numericality.odd
189
+ end
95
190
  end
96
191
 
97
- it 'rejects with the ActiveRecord :odd message' do
98
- the_matcher = matcher.odd
99
- expect do
100
- expect(validating_numericality).to the_matcher
101
- end.to fail_with_message_including(
102
- 'Expected errors to include "must be odd"'
103
- )
192
+ context 'and not validating with odd' do
193
+ it 'rejects since it does not disallow even numbers' do
194
+ record = build_record_validating_numericality
195
+ assertion = lambda do
196
+ expect(record).to validate_numericality.odd
197
+ end
198
+ expect(&assertion).to fail_with_message_including(
199
+ 'Expected errors to include "must be odd"'
200
+ )
201
+ end
104
202
  end
105
203
  end
106
204
 
107
- context 'with the even option' do
108
- it 'allows even number values for that attribute' do
109
- expect(validating_numericality(even: true)).to matcher.even
205
+ context 'qualified with even' do
206
+ context 'and validating with even' do
207
+ it 'allows even number values for that attribute' do
208
+ record = build_record_validating_numericality(even: true)
209
+ expect(record).to validate_numericality.even
210
+ end
110
211
  end
111
212
 
112
- it 'rejects when the model does not enforce even number values' do
113
- expect(validating_numericality).not_to matcher.even
213
+ context 'and not validating with even' do
214
+ it 'rejects since it does not disallow odd numbers' do
215
+ record = build_record_validating_numericality
216
+ assertion = -> { expect(record).to validate_numericality.even }
217
+ expect(&assertion).to fail_with_message_including(
218
+ 'Expected errors to include "must be even"'
219
+ )
220
+ end
114
221
  end
222
+ end
115
223
 
116
- it 'rejects with the ActiveRecord :even message' do
117
- the_matcher = matcher.even
118
- expect do
119
- expect(validating_numericality).to the_matcher
120
- end.to fail_with_message_including(
121
- 'Expected errors to include "must be even"'
122
- )
224
+ context 'qualified with is_less_than_or_equal_to' do
225
+ context 'and validating with less_than_or_equal_to' do
226
+ it 'accepts' do
227
+ record = build_record_validating_numericality(
228
+ less_than_or_equal_to: 18
229
+ )
230
+ expect(record).to validate_numericality.is_less_than_or_equal_to(18)
231
+ end
232
+
233
+ context 'if the given value is right at the allowed max value for the column' do
234
+ it 'does not raise an error' do
235
+ record = build_record_with_integer_column_of_limit(2,
236
+ less_than_or_equal_to: 32767
237
+ )
238
+ assertion = lambda do
239
+ expect(record).
240
+ to validate_numericality.
241
+ is_less_than_or_equal_to(32767)
242
+ end
243
+ expect(&assertion).not_to raise_error
244
+ end
245
+ end
123
246
  end
124
- end
125
247
 
126
- context 'qualified with less_than_or_equal_to' do
127
- it 'does not raise an error if the given value is right at the allowed max value for the column' do
128
- record = record_with_integer_column_of_limit(:attr, 2, less_than_or_equal_to: 32767)
129
- assertion = -> {
130
- expect(record).to validate_numericality_of(:attr).is_less_than_or_equal_to(32767)
131
- }
132
- expect(&assertion).not_to raise_error
248
+ context 'and not validating with less_than_or_equal_to' do
249
+ it 'rejects since it does not disallow numbers greater than the value' do
250
+ record = build_record_validating_numericality
251
+ assertion = lambda do
252
+ expect(record).
253
+ to validate_numericality.
254
+ is_less_than_or_equal_to(18)
255
+ end
256
+ expect(&assertion).to fail_with_message_including(
257
+ 'Expected errors to include "must be less than or equal to 18"'
258
+ )
259
+ end
133
260
  end
134
261
  end
135
262
 
136
- context 'qualified with less_than' do
137
- it 'does not raise an error if the given value is right at the allowed max value for the column' do
138
- record = record_with_integer_column_of_limit(:attr, 2, less_than: 32767)
139
- assertion = -> {
140
- expect(record).to validate_numericality_of(:attr).is_less_than(32767)
141
- }
142
- expect(&assertion).not_to raise_error
263
+ context 'qualified with is_less_than' do
264
+ context 'and validating with less_than' do
265
+ it 'accepts' do
266
+ record = build_record_validating_numericality(less_than: 18)
267
+ expect(record).
268
+ to validate_numericality.
269
+ is_less_than(18)
270
+ end
271
+
272
+ context 'if the given value is right at the allowed max value for the column' do
273
+ it 'does not raise an error' do
274
+ record = build_record_with_integer_column_of_limit(2, less_than: 32767)
275
+ assertion = lambda do
276
+ expect(record).to validate_numericality.is_less_than(32767)
277
+ end
278
+ expect(&assertion).not_to raise_error
279
+ end
280
+ end
281
+ end
282
+
283
+ context 'and not validating with less_than' do
284
+ it 'rejects since it does not disallow numbers greater than or equal to the value' do
285
+ record = build_record_validating_numericality
286
+ assertion = lambda do
287
+ expect(record).
288
+ to validate_numericality.
289
+ is_less_than(18)
290
+ end
291
+ expect(&assertion).to fail_with_message_including(
292
+ 'Expected errors to include "must be less than 18"'
293
+ )
294
+ end
143
295
  end
144
296
  end
145
297
 
146
- context 'qualified with equal_to' do
147
- it 'does not raise an error if the given value is right at the allowed min value for the column' do
148
- record = record_with_integer_column_of_limit(:attr, 2, equal_to: -32768)
149
- assertion = -> {
150
- expect(record).to validate_numericality_of(:attr).is_equal_to(-32768)
151
- }
152
- expect(&assertion).not_to raise_error
298
+ context 'qualified with is_equal_to' do
299
+ context 'and validating with equal_to' do
300
+ it 'accepts' do
301
+ record = build_record_validating_numericality(equal_to: 18)
302
+ expect(record).to validate_numericality.is_equal_to(18)
303
+ end
304
+
305
+ context 'if the given value is right at the allowed min value for the column' do
306
+ it 'does not raise an error' do
307
+ record = build_record_with_integer_column_of_limit(2, equal_to: -32768)
308
+ assertion = lambda do
309
+ expect(record).to validate_numericality.is_equal_to(-32768)
310
+ end
311
+ expect(&assertion).not_to raise_error
312
+ end
313
+ end
314
+
315
+ context 'if the given value is right at the allowed max value for the column' do
316
+ it 'does not raise an error' do
317
+ record = build_record_with_integer_column_of_limit(2, equal_to: 32767)
318
+ assertion = lambda do
319
+ expect(record).to validate_numericality.is_equal_to(32767)
320
+ end
321
+ expect(&assertion).not_to raise_error
322
+ end
323
+ end
153
324
  end
154
325
 
155
- it 'does not raise an error if the given value is right at the allowed max value for the column' do
156
- record = record_with_integer_column_of_limit(:attr, 2, equal_to: 32767)
157
- assertion = -> {
158
- expect(record).to validate_numericality_of(:attr).is_equal_to(32767)
159
- }
160
- expect(&assertion).not_to raise_error
326
+ context 'and not validating with equal_to' do
327
+ it 'rejects since it does not disallow numbers that are not the value' do
328
+ record = build_record_validating_numericality
329
+ assertion = lambda do
330
+ expect(record).to validate_numericality.is_equal_to(18)
331
+ end
332
+ expect(&assertion).to fail_with_message_including(
333
+ 'Expected errors to include "must be equal to 18"'
334
+ )
335
+ end
161
336
  end
162
337
  end
163
338
 
164
- context 'qualified with greater_than_or_equal to' do
165
- it 'does not raise an error if the given value is right at the allowed min value for the column' do
166
- record = record_with_integer_column_of_limit(:attr, 2,
167
- greater_than_or_equal_to: -32768
168
- )
169
- assertion = -> {
339
+ context 'qualified with is_greater_than_or_equal to' do
340
+ context 'validating with greater_than_or_equal_to' do
341
+ it 'accepts' do
342
+ record = build_record_validating_numericality(
343
+ greater_than_or_equal_to: 18
344
+ )
170
345
  expect(record).
171
- to validate_numericality_of(:attr).
172
- is_greater_than_or_equal_to(-32768)
173
- }
174
- expect(&assertion).not_to raise_error
346
+ to validate_numericality.
347
+ is_greater_than_or_equal_to(18)
348
+ end
349
+
350
+ context 'if the given value is right at the allowed min value for the column' do
351
+ it 'does not raise an error' do
352
+ record = build_record_with_integer_column_of_limit(2,
353
+ greater_than_or_equal_to: -32768
354
+ )
355
+ assertion = lambda do
356
+ expect(record).
357
+ to validate_numericality_of(:attr).
358
+ is_greater_than_or_equal_to(-32768)
359
+ end
360
+ expect(&assertion).not_to raise_error
361
+ end
362
+ end
363
+ end
364
+
365
+ context 'not validating with greater_than_or_equal_to' do
366
+ it 'rejects since it does not disallow numbers that are less than the value' do
367
+ record = build_record_validating_numericality
368
+ assertion = lambda do
369
+ expect(record).
370
+ to validate_numericality.
371
+ is_greater_than_or_equal_to(18)
372
+ end
373
+ expect(&assertion).to fail_with_message_including(
374
+ 'Expected errors to include "must be greater than or equal to 18"'
375
+ )
376
+ end
175
377
  end
176
378
  end
177
379
 
178
- context 'qualified with greater_than' do
179
- it 'does not raise an error if the given value is right at the allowed min value for the column' do
180
- record = record_with_integer_column_of_limit(:attr, 2,
181
- greater_than: -32768
182
- )
183
- assertion = -> {
380
+ context 'qualified with is_greater_than' do
381
+ context 'and validating with greater_than' do
382
+ it 'accepts' do
383
+ record = build_record_validating_numericality(greater_than: 18)
184
384
  expect(record).
185
- to validate_numericality_of(:attr).
186
- is_greater_than(-32768)
187
- }
188
- expect(&assertion).not_to raise_error
385
+ to validate_numericality.
386
+ is_greater_than(18)
387
+ end
388
+
389
+ context 'if the given value is right at the allowed min value for the column' do
390
+ it 'does not raise an error' do
391
+ record = build_record_with_integer_column_of_limit(2,
392
+ greater_than: -32768
393
+ )
394
+ assertion = lambda do
395
+ expect(record).
396
+ to validate_numericality_of(:attr).
397
+ is_greater_than(-32768)
398
+ end
399
+ expect(&assertion).not_to raise_error
400
+ end
401
+ end
402
+ end
403
+
404
+ context 'and not validating with greater_than' do
405
+ it 'rejects since it does not disallow numbers that are less than or equal to the value' do
406
+ record = build_record_validating_numericality
407
+ assertion = lambda do
408
+ expect(record).
409
+ to validate_numericality.
410
+ is_greater_than(18)
411
+ end
412
+ expect(&assertion).to fail_with_message_including(
413
+ 'Expected errors to include "must be greater than 18"'
414
+ )
415
+ end
416
+ end
417
+ end
418
+
419
+ context 'qualified with with_message' do
420
+ context 'and validating with the same message' do
421
+ it 'accepts' do
422
+ record = build_record_validating_numericality(message: 'custom')
423
+ expect(record).to validate_numericality.with_message(/custom/)
424
+ end
425
+ end
426
+
427
+ context 'and validating with a different message' do
428
+ it 'rejects' do
429
+ record = build_record_validating_numericality(message: 'custom')
430
+ expect(record).not_to validate_numericality.with_message(/wrong/)
431
+ end
432
+ end
433
+
434
+ context 'and no message is provided' do
435
+ it 'ignores the qualifier' do
436
+ record = build_record_validating_numericality
437
+ expect(record).to validate_numericality.with_message(nil)
438
+ end
189
439
  end
190
440
  end
191
441
 
192
- context 'with multiple options together' do
193
- context 'the success cases' do
194
- it do
195
- expect(validating_numericality(only_integer: true, greater_than: 18))
196
- .to matcher.only_integer.is_greater_than(18)
442
+ context 'qualified with strict' do
443
+ context 'and validating strictly' do
444
+ it 'accepts' do
445
+ record = build_record_validating_numericality(strict: true)
446
+ expect(record).to validate_numericality.strict
197
447
  end
448
+ end
198
449
 
199
- it do
200
- expect(validating_numericality(even: true, greater_than: 18))
201
- .to matcher.even.is_greater_than(18)
450
+ context 'and not validating strictly' do
451
+ it 'rejects since ActiveModel::StrictValidationFailed is never raised' do
452
+ record = build_record_validating_numericality(attribute_name: :attr)
453
+ assertion = lambda do
454
+ expect(record).to validate_numericality_of(:attr).strict
455
+ end
456
+ expect(&assertion).to fail_with_message_including(
457
+ 'Expected exception to include "Attr is not a number"'
458
+ )
202
459
  end
203
- it do
204
- expect(validating_numericality(odd: true, less_than_or_equal_to: 99))
205
- .to matcher.odd.is_less_than_or_equal_to(99)
460
+ end
461
+ end
462
+
463
+ context 'qualified with on and validating with on' do
464
+ it 'accepts' do
465
+ record = build_record_validating_numericality(on: :customizable)
466
+ expect(record).to validate_numericality.on(:customizable)
467
+ end
468
+ end
469
+
470
+ context 'qualified with on but not validating with on' do
471
+ it 'accepts since the validation never considers a context' do
472
+ record = build_record_validating_numericality
473
+ expect(record).to validate_numericality.on(:customizable)
474
+ end
475
+ end
476
+
477
+ context 'not qualified with on but validating with on' do
478
+ it 'rejects since the validation never runs' do
479
+ record = build_record_validating_numericality(on: :customizable)
480
+ assertion = lambda do
481
+ expect(record).to validate_numericality
206
482
  end
483
+ expect(&assertion).to fail_with_message_including(
484
+ 'Expected errors to include "is not a number"'
485
+ )
486
+ end
487
+ end
207
488
 
208
- it do
209
- expect(validating_numericality(
210
- only_integer: true,
211
- greater_than: 18,
212
- less_than: 99)
213
- ).to matcher.only_integer.is_greater_than(18).is_less_than(99)
489
+ context 'with combinations of qualifiers together' do
490
+ all_qualifier_combinations.each do |combination|
491
+ if combination.size > 1
492
+ it do
493
+ validation_options = build_validation_options(for: combination)
494
+ record = build_record_validating_numericality(validation_options)
495
+ validate_numericality = self.validate_numericality
496
+ apply_qualifiers!(for: combination, to: validate_numericality)
497
+ expect(record).to validate_numericality
498
+ end
214
499
  end
215
500
  end
216
501
 
217
- context 'the failure cases with different validators' do
218
- it do
219
- expect(validating_numericality(even: true, greater_than: 18))
220
- .not_to matcher.only_integer.is_greater_than(18)
502
+ context 'when the qualifiers do not match the validation options' do
503
+ specify 'such as validating even but testing that only_integer is validated' do
504
+ record = build_record_validating_numericality(
505
+ even: true,
506
+ greater_than: 18
507
+ )
508
+ assertion = lambda do
509
+ expect(record).
510
+ to validate_numericality.
511
+ only_integer.
512
+ is_greater_than(18)
513
+ end
514
+ message = format_message_according_to_rails_version(
515
+ <<-MESSAGE.strip_heredoc
516
+ Expected errors to include "must be an integer" when attr is set to 0.1,
517
+ got errors:
518
+ * "must be greater than 18" (attribute: attr, value: "0.1")
519
+ MESSAGE
520
+ )
521
+ expect(&assertion).to fail_with_message(message)
221
522
  end
222
523
 
223
- it do
224
- expect(validating_numericality(greater_than: 18))
225
- .not_to matcher.only_integer.is_greater_than(18)
524
+ specify 'such as not validating only_integer but testing that only_integer is validated' do
525
+ record = build_record_validating_numericality(greater_than: 18)
526
+ assertion = lambda do
527
+ expect(record).
528
+ to validate_numericality.
529
+ only_integer.
530
+ is_greater_than(18)
531
+ end
532
+ message = format_message_according_to_rails_version(
533
+ <<-MESSAGE.strip_heredoc
534
+ Expected errors to include "must be an integer" when attr is set to 0.1,
535
+ got errors:
536
+ * "must be greater than 18" (attribute: attr, value: "0.1")
537
+ MESSAGE
538
+ )
539
+ expect(&assertion).to fail_with_message(message)
226
540
  end
227
541
 
228
- it do
229
- expect(
230
- validating_numericality(even: true, greater_than_or_equal_to: 18)
231
- ).not_to matcher.even.is_greater_than(18)
542
+ specify 'such as validating greater_than_or_equal_to (+ even) but testing that greater_than is validated' do
543
+ record = build_record_validating_numericality(
544
+ even: true,
545
+ greater_than_or_equal_to: 18
546
+ )
547
+ assertion = lambda do
548
+ expect(record).
549
+ to validate_numericality.
550
+ even.
551
+ is_greater_than(18)
552
+ end
553
+ message = format_message_according_to_rails_version(
554
+ <<-MESSAGE.strip_heredoc
555
+ Expected errors to include "must be greater than 18" when attr is set to 18,
556
+ got no errors
557
+ MESSAGE
558
+ )
559
+ expect(&assertion).to fail_with_message(message)
232
560
  end
233
561
 
234
- it do
235
- expect(validating_numericality(odd: true, greater_than: 18))
236
- .not_to matcher.even.is_greater_than(18)
562
+ specify 'such as validating odd (+ greater_than) but testing that even is validated' do
563
+ record = build_record_validating_numericality(
564
+ odd: true,
565
+ greater_than: 18
566
+ )
567
+ assertion = lambda do
568
+ expect(record).
569
+ to validate_numericality.
570
+ even.
571
+ is_greater_than(18)
572
+ end
573
+ message = format_message_according_to_rails_version(
574
+ <<-MESSAGE.strip_heredoc
575
+ Expected errors to include "must be even" when attr is set to 1,
576
+ got errors:
577
+ * "must be greater than 18" (attribute: attr, value: "1")
578
+ MESSAGE
579
+ )
580
+ expect(&assertion).to fail_with_message(message)
237
581
  end
238
582
 
239
- it do
240
- expect(validating_numericality(
241
- odd: true,
242
- greater_than_or_equal_to: 99
243
- )
244
- ).not_to matcher.odd.is_less_than_or_equal_to(99)
583
+ specify 'such as validating greater_than_or_equal_to (+ odd) but testing that is_less_than_or_equal_to is validated' do
584
+ record = build_record_validating_numericality(
585
+ odd: true,
586
+ greater_than_or_equal_to: 99
587
+ )
588
+ assertion = lambda do
589
+ expect(record).
590
+ to validate_numericality.
591
+ odd.
592
+ is_less_than_or_equal_to(99)
593
+ end
594
+ message = format_message_according_to_rails_version(
595
+ <<-MESSAGE.strip_heredoc
596
+ Expected errors to include "must be less than or equal to 99" when attr is set to 101,
597
+ got no errors
598
+ MESSAGE
599
+ )
600
+ expect(&assertion).to fail_with_message(message)
245
601
  end
246
602
 
247
- it do
248
- expect(validating_numericality(
249
- only_integer: true,
250
- greater_than_or_equal_to: 18,
251
- less_than: 99
252
- )
253
- ).not_to matcher.only_integer.is_greater_than(18).is_less_than(99)
603
+ specify 'such as validating greater_than_or_equal_to (+ only_integer + less_than) but testing that greater_than is validated' do
604
+ record = build_record_validating_numericality(
605
+ only_integer: true,
606
+ greater_than_or_equal_to: 18,
607
+ less_than: 99
608
+ )
609
+ assertion = lambda do
610
+ expect(record).
611
+ to validate_numericality.
612
+ only_integer.
613
+ is_greater_than(18).
614
+ is_less_than(99)
615
+ end
616
+ message = format_message_according_to_rails_version(
617
+ <<-MESSAGE.strip_heredoc
618
+ Expected errors to include "must be greater than 18" when attr is set to 18,
619
+ got no errors
620
+ MESSAGE
621
+ )
622
+ expect(&assertion).to fail_with_message(message)
254
623
  end
255
624
  end
256
625
 
257
- context 'the failure cases with wrong values' do
258
- it do
259
- expect(validating_numericality(only_integer: true, greater_than: 19))
260
- .not_to matcher.only_integer.is_greater_than(18)
626
+ context 'when qualifiers match the validation options but the values are different' do
627
+ specify 'such as testing greater_than (+ only_integer) with lower value' do
628
+ record = build_record_validating_numericality(
629
+ only_integer: true,
630
+ greater_than: 19
631
+ )
632
+ assertion = lambda do
633
+ expect(record).
634
+ to validate_numericality.
635
+ only_integer.
636
+ is_greater_than(18)
637
+ end
638
+ message = format_message_according_to_rails_version(
639
+ <<-MESSAGE.strip_heredoc
640
+ Expected errors to include "must be greater than 18" when attr is set to 18,
641
+ got errors:
642
+ * "must be greater than 19" (attribute: attr, value: "19")
643
+ MESSAGE
644
+ )
645
+ expect(&assertion).to fail_with_message(message)
261
646
  end
262
647
 
263
- it do
264
- expect(validating_numericality(only_integer: true, greater_than: 17))
265
- .not_to matcher.only_integer.is_greater_than(18)
648
+ specify 'such as testing greater_than (+ only_integer) with higher value' do
649
+ record = build_record_validating_numericality(
650
+ only_integer: true,
651
+ greater_than: 17
652
+ )
653
+ assertion = lambda do
654
+ expect(record).
655
+ to validate_numericality.
656
+ only_integer.
657
+ is_greater_than(18)
658
+ end
659
+ message = format_message_according_to_rails_version(
660
+ <<-MESSAGE.strip_heredoc
661
+ Expected errors to include "must be greater than 18" when attr is set to 18,
662
+ got no errors
663
+ MESSAGE
664
+ )
665
+ expect(&assertion).to fail_with_message(message)
266
666
  end
267
667
 
268
- it do
269
- expect(validating_numericality(even: true, greater_than: 20))
270
- .not_to matcher.even.is_greater_than(18)
668
+ specify 'such as testing greater_than (+ even) with lower value' do
669
+ record = build_record_validating_numericality(
670
+ even: true,
671
+ greater_than: 20
672
+ )
673
+ assertion = lambda do
674
+ expect(record).
675
+ to validate_numericality.
676
+ even.
677
+ is_greater_than(18)
678
+ end
679
+ message = format_message_according_to_rails_version(
680
+ <<-MESSAGE.strip_heredoc
681
+ Expected errors to include "must be greater than 18" when attr is set to 18,
682
+ got errors:
683
+ * "must be greater than 20" (attribute: attr, value: "20")
684
+ MESSAGE
685
+ )
686
+ expect(&assertion).to fail_with_message(message)
271
687
  end
272
688
 
273
- it do
274
- expect(validating_numericality(even: true, greater_than: 16))
275
- .not_to matcher.even.is_greater_than(18)
689
+ specify 'such as testing greater than (+ even) with higher value' do
690
+ record = build_record_validating_numericality(
691
+ even: true,
692
+ greater_than: 16
693
+ )
694
+ assertion = lambda do
695
+ expect(record).
696
+ to validate_numericality.
697
+ even.
698
+ is_greater_than(18)
699
+ end
700
+ message = format_message_according_to_rails_version(
701
+ <<-MESSAGE.strip_heredoc
702
+ Expected errors to include "must be greater than 18" when attr is set to 18,
703
+ got no errors
704
+ MESSAGE
705
+ )
706
+ expect(&assertion).to fail_with_message(message)
276
707
  end
277
708
 
278
- it do
279
- expect(validating_numericality(odd: true, less_than_or_equal_to: 101))
280
- .not_to matcher.odd.is_less_than_or_equal_to(99)
709
+ specify 'such as testing less_than_or_equal_to (+ odd) with lower value' do
710
+ record = build_record_validating_numericality(
711
+ odd: true,
712
+ less_than_or_equal_to: 101
713
+ )
714
+ assertion = lambda do
715
+ expect(record).
716
+ to validate_numericality.
717
+ odd.
718
+ is_less_than_or_equal_to(99)
719
+ end
720
+ message = format_message_according_to_rails_version(
721
+ <<-MESSAGE.strip_heredoc
722
+ Expected errors to include "must be less than or equal to 99" when attr is set to 101,
723
+ got no errors
724
+ MESSAGE
725
+ )
726
+ expect(&assertion).to fail_with_message(message)
281
727
  end
282
728
 
283
- it do
284
- expect(validating_numericality(odd: true, less_than_or_equal_to: 97))
285
- .not_to matcher.odd.is_less_than_or_equal_to(99)
729
+ specify 'such as testing less_than_or_equal_to (+ odd) with higher value' do
730
+ record = build_record_validating_numericality(
731
+ odd: true,
732
+ less_than_or_equal_to: 97
733
+ )
734
+ assertion = lambda do
735
+ expect(record).
736
+ to validate_numericality.
737
+ odd.
738
+ is_less_than_or_equal_to(99)
739
+ end
740
+ message = format_message_according_to_rails_version(
741
+ <<-MESSAGE.strip_heredoc
742
+ Expected errors to include "must be less than or equal to 99" when attr is set to 101,
743
+ got errors:
744
+ * "must be less than or equal to 97" (attribute: attr, value: "101")
745
+ MESSAGE
746
+ )
747
+ expect(&assertion).to fail_with_message(message)
286
748
  end
287
749
 
288
- it do
289
- expect(validating_numericality(only_integer: true,
290
- greater_than: 19,
291
- less_than: 99))
292
- .not_to matcher.only_integer.is_greater_than(18).is_less_than(99)
750
+ specify 'such as testing greater_than (+ only_integer + less_than) with lower value' do
751
+ record = build_record_validating_numericality(
752
+ only_integer: true,
753
+ greater_than: 19,
754
+ less_than: 99
755
+ )
756
+ assertion = lambda do
757
+ expect(record).
758
+ to validate_numericality.
759
+ only_integer.
760
+ is_greater_than(18).
761
+ is_less_than(99)
762
+ end
763
+ message = format_message_according_to_rails_version(
764
+ <<-MESSAGE.strip_heredoc
765
+ Expected errors to include "must be greater than 18" when attr is set to 18,
766
+ got errors:
767
+ * "must be greater than 19" (attribute: attr, value: "19")
768
+ MESSAGE
769
+ )
770
+ expect(&assertion).to fail_with_message(message)
293
771
  end
294
772
 
295
- it do
296
- expect(validating_numericality(only_integer: true,
297
- greater_than: 18,
298
- less_than: 100))
299
- .not_to matcher.only_integer.is_greater_than(18).is_less_than(99)
773
+ specify 'such as testing less_than (+ only_integer + greater_than) with higher value' do
774
+ record = build_record_validating_numericality(
775
+ only_integer: true,
776
+ greater_than: 18,
777
+ less_than: 100
778
+ )
779
+ assertion = lambda do
780
+ expect(record).
781
+ to validate_numericality.
782
+ only_integer.
783
+ is_greater_than(18).
784
+ is_less_than(99)
785
+ end
786
+ message = format_message_according_to_rails_version(
787
+ <<-MESSAGE.strip_heredoc
788
+ Expected errors to include "must be less than 99" when attr is set to 100,
789
+ got errors:
790
+ * "must be less than 100" (attribute: attr, value: "100")
791
+ MESSAGE
792
+ )
793
+ expect(&assertion).to fail_with_message(message)
300
794
  end
301
795
  end
302
796
  end
303
797
 
304
798
  context 'with large numbers' do
305
799
  it do
306
- expect(validating_numericality(greater_than: 100_000))
307
- .to matcher.is_greater_than(100_000)
800
+ record = build_record_validating_numericality(greater_than: 100_000)
801
+ expect(record).to validate_numericality.is_greater_than(100_000)
308
802
  end
309
803
 
310
804
  it do
311
- expect(validating_numericality(less_than: 100_000))
312
- .to matcher.is_less_than(100_000)
805
+ record = build_record_validating_numericality(less_than: 100_000)
806
+ expect(record).to validate_numericality.is_less_than(100_000)
313
807
  end
314
808
 
315
809
  it do
316
- expect(validating_numericality(greater_than_or_equal_to: 100_000))
317
- .to matcher.is_greater_than_or_equal_to(100_000)
810
+ record = build_record_validating_numericality(
811
+ greater_than_or_equal_to: 100_000
812
+ )
813
+ expect(record).
814
+ to validate_numericality.
815
+ is_greater_than_or_equal_to(100_000)
318
816
  end
319
817
 
320
818
  it do
321
- expect(validating_numericality(less_than_or_equal_to: 100_000))
322
- .to matcher.is_less_than_or_equal_to(100_000)
323
- end
324
- end
325
-
326
- context 'with a custom validation message' do
327
- it 'accepts when the messages match' do
328
- expect(validating_numericality(message: 'custom')).
329
- to matcher.with_message(/custom/)
330
- end
331
-
332
- it 'rejects when the messages do not match' do
333
- expect(validating_numericality(message: 'custom')).
334
- not_to matcher.with_message(/wrong/)
819
+ record = build_record_validating_numericality(
820
+ less_than_or_equal_to: 100_000
821
+ )
822
+ expect(record).
823
+ to validate_numericality.
824
+ is_less_than_or_equal_to(100_000)
335
825
  end
336
826
  end
337
827
 
338
828
  context 'when the subject is stubbed' do
339
- it 'retains stubs on submatchers' do
340
- subject = define_model :example, attr: :string do
829
+ it 'retains that stub while the validate_numericality is matching' do
830
+ model = define_model :example, attr: :string do
341
831
  validates_numericality_of :attr, odd: true
342
832
  before_validation :set_attr!
343
833
  def set_attr!; self.attr = 5 end
344
- end.new
834
+ end
345
835
 
346
- allow(subject).to receive(:set_attr!)
347
- expect(subject).to matcher.odd
836
+ record = model.new
837
+ allow(record).to receive(:set_attr!)
838
+
839
+ expect(record).to validate_numericality_of(:attr).odd
348
840
  end
349
841
  end
350
842
 
351
843
  describe '#description' do
352
- context 'without submatchers' do
353
- it { expect(matcher.description).to eq 'only allow numbers for attr' }
844
+ context 'qualified with nothing' do
845
+ it 'describes that it allows numbers' do
846
+ matcher = validate_numericality_of(:attr)
847
+ expect(matcher.description).to eq 'only allow numbers for attr'
848
+ end
354
849
  end
355
850
 
356
- context 'with only integer option' do
357
- it do
358
- expect(matcher.only_integer.description)
359
- .to eq 'only allow integers for attr'
851
+ context 'qualified with only_integer' do
852
+ it 'describes that it allows integers' do
853
+ matcher = validate_numericality_of(:attr).only_integer
854
+ expect(matcher.description).to eq 'only allow integers for attr'
360
855
  end
361
856
  end
362
857
 
363
- [:odd, :even].each do |type|
364
- context "with #{type} option" do
365
- it do
366
- expect(matcher.__send__(type).description)
367
- .to eq "only allow #{type} numbers for attr"
858
+ qualifiers_under(:cardinality).each do |qualifier|
859
+ context "qualified with #{qualifier[:name]}" do
860
+ it "describes that it allows #{qualifier[:name]} numbers" do
861
+ matcher = validate_numericality_of(:attr).__send__(qualifier[:name])
862
+ expect(matcher.description).
863
+ to eq "only allow #{qualifier[:name]} numbers for attr"
368
864
  end
369
865
  end
370
866
  end
371
867
 
372
- [:is_greater_than,
373
- :is_greater_than_or_equal_to,
374
- :is_less_than,
375
- :is_less_than_or_equal_to,
376
- :is_equal_to ].each do |comparison|
377
- context "with #{comparison} option" do
378
- it do
379
- expect(matcher.__send__(comparison, 18).description)
380
- .to eq(
381
- 'only allow numbers for attr which are ' +
382
- "#{comparison.to_s.sub('is_', '').gsub('_', ' ')} 18"
868
+ qualifiers_under(:comparison).each do |qualifier|
869
+ comparison_phrase = qualifier[:validation_name].to_s.gsub('_', ' ')
870
+
871
+ context "qualified with #{qualifier[:name]}" do
872
+ it "describes that it allows numbers #{comparison_phrase} a certain value" do
873
+ matcher = validate_numericality_of(:attr).
874
+ __send__(qualifier[:name], 18)
875
+
876
+ expect(matcher.description).to eq(
877
+ "only allow numbers for attr which are #{comparison_phrase} 18"
383
878
  )
384
879
  end
385
880
  end
386
881
  end
387
882
 
388
- context 'with odd, is_greater_than_or_equal_to option' do
389
- it do
390
- expect(matcher.odd.is_greater_than_or_equal_to(18).description)
391
- .to eq(
392
- 'only allow odd numbers for attr ' +
393
- 'which are greater than or equal to 18'
883
+ context 'qualified with odd + is_greater_than_or_equal_to' do
884
+ it "describes that it allows odd numbers greater than or equal to a certain value" do
885
+ matcher = validate_numericality_of(:attr).
886
+ odd.
887
+ is_greater_than_or_equal_to(18)
888
+
889
+ expect(matcher.description).to eq(
890
+ 'only allow odd numbers for attr which are greater than or equal to 18'
891
+ )
892
+ end
893
+ end
894
+
895
+ context 'qualified with only integer + is_greater_than + less_than_or_equal_to' do
896
+ it 'describes that it allows integer greater than one value and less than or equal to another' do
897
+ matcher = validate_numericality_of(:attr).
898
+ only_integer.
899
+ is_greater_than(18).
900
+ is_less_than_or_equal_to(100)
901
+
902
+ expect(matcher.description).to eq(
903
+ 'only allow integers for attr which are greater than 18 and less than or equal to 100'
904
+ )
905
+ end
906
+ end
907
+
908
+ context 'qualified with strict' do
909
+ it 'describes that it relies upon a strict validation' do
910
+ matcher = validate_numericality_of(:attr).strict
911
+ expect(matcher.description).to eq(
912
+ 'only allow numbers for attr, strictly'
913
+ )
914
+ end
915
+
916
+ context 'and qualified with a comparison qualifier' do
917
+ it 'places the comparison description after "strictly"' do
918
+ matcher = validate_numericality_of(:attr).is_less_than(18).strict
919
+ expect(matcher.description).to eq(
920
+ 'only allow numbers for attr, strictly, which are less than 18'
394
921
  )
922
+ end
395
923
  end
396
924
  end
925
+ end
397
926
 
398
- context 'with only integer, is_greater_than and less_than_or_equal_to option' do
399
- it { expect(matcher.only_integer.is_greater_than(18).is_less_than_or_equal_to(100).description).
400
- to eq "only allow integers for attr which are greater than 18 and less than or equal to 100" }
927
+ def build_validation_options(args)
928
+ combination = args.fetch(:for)
929
+
930
+ combination.each_with_object({}) do |qualifier, hash|
931
+ value = self.class.default_validation_values.fetch(qualifier[:validation_name])
932
+ hash[qualifier[:validation_name]] = value
401
933
  end
402
934
  end
403
935
 
936
+ def apply_qualifiers!(args)
937
+ combination = args.fetch(:for)
938
+ matcher = args.fetch(:to)
939
+
940
+ combination.each do |qualifier|
941
+ args = self.class.default_qualifier_arguments.fetch(qualifier[:name])
942
+ matcher.__send__(qualifier[:name], *args)
943
+ end
944
+ end
945
+
946
+ def define_model_validating_numericality(options = {})
947
+ attribute_name = options.delete(:attribute_name) { self.attribute_name }
948
+
949
+ define_model 'Example', attribute_name => :string do |model|
950
+ model.validates_numericality_of(attribute_name, options)
951
+ end
952
+ end
404
953
 
405
- def validating_numericality(options = {})
406
- define_model :example, attr: :string do
407
- validates_numericality_of :attr, options
408
- end.new
954
+ def build_record_validating_numericality(options = {})
955
+ define_model_validating_numericality(options).new
409
956
  end
410
957
 
411
- def not_validating_numericality
412
- define_model(:example, attr: :string).new
958
+ def define_model_validating_nothing
959
+ define_model('Example', attribute_name => :string)
413
960
  end
414
961
 
415
- def matcher
416
- validate_numericality_of(:attr)
962
+ def build_record_validating_nothing
963
+ define_model_validating_nothing.new
417
964
  end
418
965
 
419
- def record_with_integer_column_of_limit(attribute, limit, validation_options = {})
966
+ def validate_numericality
967
+ validate_numericality_of(attribute_name)
968
+ end
969
+
970
+ def attribute_name
971
+ :attr
972
+ end
973
+
974
+ def define_model_with_integer_column_of_limit(limit, validation_options = {})
420
975
  column_options = { type: :integer, options: { limit: limit } }
421
- define_model :example, attribute => column_options do
422
- validates_numericality_of attribute, validation_options
423
- end.new
976
+ define_model :example, attribute_name => column_options do |model|
977
+ model.validates_numericality_of(attribute_name, validation_options)
978
+ end
979
+ end
980
+
981
+ def build_record_with_integer_column_of_limit(limit, validation_options = {})
982
+ define_model_with_integer_column_of_limit(limit, validation_options).new
983
+ end
984
+
985
+ def format_message_according_to_rails_version(message)
986
+ if rails_gte_4_2?
987
+ message
988
+ else
989
+ message.gsub(/"((?:\d+)(?:\.\d+)?)"/, '\1')
990
+ end
424
991
  end
425
992
  end