mcmire-shoulda-matchers 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.travis.yml +32 -0
  4. data/.yardopts +7 -0
  5. data/Appraisals +45 -0
  6. data/CONTRIBUTING.md +41 -0
  7. data/Gemfile +31 -0
  8. data/Gemfile.lock +166 -0
  9. data/MIT-LICENSE +22 -0
  10. data/NEWS.md +299 -0
  11. data/README.md +163 -0
  12. data/Rakefile +116 -0
  13. data/doc_config/gh-pages/index.html.erb +9 -0
  14. data/doc_config/yard/setup.rb +22 -0
  15. data/doc_config/yard/templates/default/fulldoc/html/css/bootstrap.css +5967 -0
  16. data/doc_config/yard/templates/default/fulldoc/html/css/full_list.css +12 -0
  17. data/doc_config/yard/templates/default/fulldoc/html/css/global.css +45 -0
  18. data/doc_config/yard/templates/default/fulldoc/html/css/solarized.css +69 -0
  19. data/doc_config/yard/templates/default/fulldoc/html/css/style.css +283 -0
  20. data/doc_config/yard/templates/default/fulldoc/html/full_list.erb +32 -0
  21. data/doc_config/yard/templates/default/fulldoc/html/full_list_class.erb +1 -0
  22. data/doc_config/yard/templates/default/fulldoc/html/full_list_method.erb +8 -0
  23. data/doc_config/yard/templates/default/fulldoc/html/js/app.js +300 -0
  24. data/doc_config/yard/templates/default/fulldoc/html/js/full_list.js +1 -0
  25. data/doc_config/yard/templates/default/fulldoc/html/js/jquery.stickyheaders.js +289 -0
  26. data/doc_config/yard/templates/default/fulldoc/html/js/underscore.min.js +6 -0
  27. data/doc_config/yard/templates/default/fulldoc/html/setup.rb +8 -0
  28. data/doc_config/yard/templates/default/layout/html/breadcrumb.erb +14 -0
  29. data/doc_config/yard/templates/default/layout/html/fonts.erb +1 -0
  30. data/doc_config/yard/templates/default/layout/html/layout.erb +23 -0
  31. data/doc_config/yard/templates/default/layout/html/search.erb +13 -0
  32. data/doc_config/yard/templates/default/layout/html/setup.rb +8 -0
  33. data/doc_config/yard/templates/default/method_details/html/source.erb +10 -0
  34. data/doc_config/yard/templates/default/module/html/box_info.erb +31 -0
  35. data/features/rails_integration.feature +113 -0
  36. data/features/step_definitions/rails_steps.rb +162 -0
  37. data/features/support/env.rb +5 -0
  38. data/gemfiles/3.0.gemfile +24 -0
  39. data/gemfiles/3.0.gemfile.lock +150 -0
  40. data/gemfiles/3.1.gemfile +27 -0
  41. data/gemfiles/3.1.gemfile.lock +173 -0
  42. data/gemfiles/3.2.gemfile +27 -0
  43. data/gemfiles/3.2.gemfile.lock +171 -0
  44. data/gemfiles/4.0.0.gemfile +28 -0
  45. data/gemfiles/4.0.0.gemfile.lock +172 -0
  46. data/gemfiles/4.0.1.gemfile +28 -0
  47. data/gemfiles/4.0.1.gemfile.lock +172 -0
  48. data/lib/shoulda-matchers.rb +1 -0
  49. data/lib/shoulda/matchers.rb +11 -0
  50. data/lib/shoulda/matchers/action_controller.rb +17 -0
  51. data/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +64 -0
  52. data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +97 -0
  53. data/lib/shoulda/matchers/action_controller/render_template_matcher.rb +81 -0
  54. data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +117 -0
  55. data/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +114 -0
  56. data/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +154 -0
  57. data/lib/shoulda/matchers/action_controller/route_matcher.rb +116 -0
  58. data/lib/shoulda/matchers/action_controller/route_params.rb +48 -0
  59. data/lib/shoulda/matchers/action_controller/set_session_matcher.rb +164 -0
  60. data/lib/shoulda/matchers/action_controller/set_the_flash_matcher.rb +296 -0
  61. data/lib/shoulda/matchers/active_model.rb +30 -0
  62. data/lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb +167 -0
  63. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +314 -0
  64. data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +46 -0
  65. data/lib/shoulda/matchers/active_model/ensure_exclusion_of_matcher.rb +160 -0
  66. data/lib/shoulda/matchers/active_model/ensure_inclusion_of_matcher.rb +417 -0
  67. data/lib/shoulda/matchers/active_model/ensure_length_of_matcher.rb +337 -0
  68. data/lib/shoulda/matchers/active_model/errors.rb +10 -0
  69. data/lib/shoulda/matchers/active_model/exception_message_finder.rb +58 -0
  70. data/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +92 -0
  71. data/lib/shoulda/matchers/active_model/helpers.rb +46 -0
  72. data/lib/shoulda/matchers/active_model/numericality_matchers.rb +9 -0
  73. data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +75 -0
  74. data/lib/shoulda/matchers/active_model/numericality_matchers/even_number_matcher.rb +27 -0
  75. data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +41 -0
  76. data/lib/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher.rb +27 -0
  77. data/lib/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher.rb +26 -0
  78. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +112 -0
  79. data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +77 -0
  80. data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +121 -0
  81. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +380 -0
  82. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +89 -0
  83. data/lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb +372 -0
  84. data/lib/shoulda/matchers/active_model/validation_matcher.rb +97 -0
  85. data/lib/shoulda/matchers/active_model/validation_message_finder.rb +69 -0
  86. data/lib/shoulda/matchers/active_record.rb +22 -0
  87. data/lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb +204 -0
  88. data/lib/shoulda/matchers/active_record/association_matcher.rb +901 -0
  89. data/lib/shoulda/matchers/active_record/association_matchers.rb +9 -0
  90. data/lib/shoulda/matchers/active_record/association_matchers/counter_cache_matcher.rb +41 -0
  91. data/lib/shoulda/matchers/active_record/association_matchers/dependent_matcher.rb +41 -0
  92. data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +81 -0
  93. data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +65 -0
  94. data/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +94 -0
  95. data/lib/shoulda/matchers/active_record/association_matchers/order_matcher.rb +41 -0
  96. data/lib/shoulda/matchers/active_record/association_matchers/source_matcher.rb +41 -0
  97. data/lib/shoulda/matchers/active_record/association_matchers/through_matcher.rb +63 -0
  98. data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +261 -0
  99. data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +149 -0
  100. data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +72 -0
  101. data/lib/shoulda/matchers/active_record/serialize_matcher.rb +181 -0
  102. data/lib/shoulda/matchers/assertion_error.rb +19 -0
  103. data/lib/shoulda/matchers/error.rb +6 -0
  104. data/lib/shoulda/matchers/integrations/rspec.rb +20 -0
  105. data/lib/shoulda/matchers/integrations/test_unit.rb +30 -0
  106. data/lib/shoulda/matchers/rails_shim.rb +50 -0
  107. data/lib/shoulda/matchers/version.rb +6 -0
  108. data/lib/shoulda/matchers/warn.rb +8 -0
  109. data/shoulda-matchers.gemspec +23 -0
  110. data/spec/shoulda/matchers/action_controller/filter_param_matcher_spec.rb +22 -0
  111. data/spec/shoulda/matchers/action_controller/redirect_to_matcher_spec.rb +42 -0
  112. data/spec/shoulda/matchers/action_controller/render_template_matcher_spec.rb +78 -0
  113. data/spec/shoulda/matchers/action_controller/render_with_layout_matcher_spec.rb +63 -0
  114. data/spec/shoulda/matchers/action_controller/rescue_from_matcher_spec.rb +63 -0
  115. data/spec/shoulda/matchers/action_controller/respond_with_matcher_spec.rb +31 -0
  116. data/spec/shoulda/matchers/action_controller/route_matcher_spec.rb +70 -0
  117. data/spec/shoulda/matchers/action_controller/route_params_spec.rb +30 -0
  118. data/spec/shoulda/matchers/action_controller/set_session_matcher_spec.rb +51 -0
  119. data/spec/shoulda/matchers/action_controller/set_the_flash_matcher_spec.rb +153 -0
  120. data/spec/shoulda/matchers/active_model/allow_mass_assignment_of_matcher_spec.rb +111 -0
  121. data/spec/shoulda/matchers/active_model/allow_value_matcher_spec.rb +170 -0
  122. data/spec/shoulda/matchers/active_model/disallow_value_matcher_spec.rb +81 -0
  123. data/spec/shoulda/matchers/active_model/ensure_exclusion_of_matcher_spec.rb +95 -0
  124. data/spec/shoulda/matchers/active_model/ensure_inclusion_of_matcher_spec.rb +320 -0
  125. data/spec/shoulda/matchers/active_model/ensure_length_of_matcher_spec.rb +166 -0
  126. data/spec/shoulda/matchers/active_model/exception_message_finder_spec.rb +111 -0
  127. data/spec/shoulda/matchers/active_model/have_secure_password_matcher_spec.rb +20 -0
  128. data/spec/shoulda/matchers/active_model/helpers_spec.rb +158 -0
  129. data/spec/shoulda/matchers/active_model/numericality_matchers/comparison_matcher_spec.rb +169 -0
  130. data/spec/shoulda/matchers/active_model/numericality_matchers/even_number_matcher_spec.rb +59 -0
  131. data/spec/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher_spec.rb +59 -0
  132. data/spec/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher_spec.rb +57 -0
  133. data/spec/shoulda/matchers/active_model/validate_absence_of_matcher_spec.rb +139 -0
  134. data/spec/shoulda/matchers/active_model/validate_acceptance_of_matcher_spec.rb +41 -0
  135. data/spec/shoulda/matchers/active_model/validate_confirmation_of_matcher_spec.rb +47 -0
  136. data/spec/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +331 -0
  137. data/spec/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +180 -0
  138. data/spec/shoulda/matchers/active_model/validate_uniqueness_of_matcher_spec.rb +398 -0
  139. data/spec/shoulda/matchers/active_model/validation_message_finder_spec.rb +127 -0
  140. data/spec/shoulda/matchers/active_record/accept_nested_attributes_for_matcher_spec.rb +107 -0
  141. data/spec/shoulda/matchers/active_record/association_matcher_spec.rb +860 -0
  142. data/spec/shoulda/matchers/active_record/association_matchers/model_reflection_spec.rb +247 -0
  143. data/spec/shoulda/matchers/active_record/have_db_column_matcher_spec.rb +111 -0
  144. data/spec/shoulda/matchers/active_record/have_db_index_matcher_spec.rb +78 -0
  145. data/spec/shoulda/matchers/active_record/have_readonly_attributes_matcher_spec.rb +41 -0
  146. data/spec/shoulda/matchers/active_record/serialize_matcher_spec.rb +86 -0
  147. data/spec/spec_helper.rb +26 -0
  148. data/spec/support/active_model_versions.rb +13 -0
  149. data/spec/support/active_resource_builder.rb +29 -0
  150. data/spec/support/activemodel_helpers.rb +19 -0
  151. data/spec/support/capture_helpers.rb +19 -0
  152. data/spec/support/class_builder.rb +42 -0
  153. data/spec/support/controller_builder.rb +74 -0
  154. data/spec/support/fail_with_message_including_matcher.rb +33 -0
  155. data/spec/support/fail_with_message_matcher.rb +32 -0
  156. data/spec/support/i18n_faker.rb +10 -0
  157. data/spec/support/mailer_builder.rb +10 -0
  158. data/spec/support/model_builder.rb +81 -0
  159. data/spec/support/rails_versions.rb +18 -0
  160. data/spec/support/shared_examples/numerical_submatcher.rb +19 -0
  161. data/spec/support/shared_examples/numerical_type_submatcher.rb +17 -0
  162. data/spec/support/test_application.rb +120 -0
  163. data/yard.watchr +5 -0
  164. metadata +281 -0
@@ -0,0 +1,111 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shoulda::Matchers::ActiveModel::AllowMassAssignmentOfMatcher do
4
+ context '#description' do
5
+ context 'without a role' do
6
+ it 'includes the attribute name' do
7
+ expect(described_class.new(:attr).description).
8
+ to eq 'allow mass assignment of attr'
9
+ end
10
+ end
11
+
12
+ if active_model_3_1?
13
+ context 'with a role' do
14
+ it 'includes the attribute name and the role' do
15
+ expect(described_class.new(:attr).as(:admin).description).
16
+ to eq 'allow mass assignment of attr as admin'
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ context 'an attribute that is blacklisted from mass-assignment' do
23
+ it 'rejects being mass-assignable' do
24
+ model = define_model(:example, blacklisted: :string) do
25
+ attr_protected :blacklisted
26
+ end.new
27
+
28
+ expect(model).not_to allow_mass_assignment_of(:blacklisted)
29
+ end
30
+ end
31
+
32
+ context 'an attribute that is not whitelisted for mass-assignment' do
33
+ it 'rejects being mass-assignable' do
34
+ model = define_model(:example, not_whitelisted: :string,
35
+ whitelisted: :string) do
36
+ attr_accessible :whitelisted
37
+ end.new
38
+
39
+ expect(model).not_to allow_mass_assignment_of(:not_whitelisted)
40
+ end
41
+ end
42
+
43
+ context 'an attribute that is whitelisted for mass-assignment' do
44
+ it 'accepts being mass-assignable' do
45
+ expect(define_model(:example, whitelisted: :string) do
46
+ attr_accessible :whitelisted
47
+ end.new).to allow_mass_assignment_of(:whitelisted)
48
+ end
49
+ end
50
+
51
+ context 'an attribute not included in the mass-assignment blacklist' do
52
+ it 'accepts being mass-assignable' do
53
+ model = define_model(:example, not_blacklisted: :string,
54
+ blacklisted: :string) do
55
+ attr_protected :blacklisted
56
+ end.new
57
+
58
+ expect(model).to allow_mass_assignment_of(:not_blacklisted)
59
+ end
60
+ end
61
+
62
+ unless active_model_3_2? || active_model_4_0?
63
+ context 'an attribute on a class with no protected attributes' do
64
+ it 'accepts being mass-assignable' do
65
+ expect(no_protected_attributes).to allow_mass_assignment_of(:attr)
66
+ end
67
+
68
+ it 'assigns a negative failure message' do
69
+ matcher = allow_mass_assignment_of(:attr)
70
+
71
+ expect(matcher.matches?(no_protected_attributes)).to eq true
72
+
73
+ expect(matcher.failure_message_when_negated).not_to be_nil
74
+ end
75
+ end
76
+
77
+ def no_protected_attributes
78
+ define_model(:example, attr: :string).new
79
+ end
80
+ end
81
+
82
+ context 'an attribute on a class with all protected attributes' do
83
+ it 'rejects being mass-assignable' do
84
+ expect(all_protected_attributes).not_to allow_mass_assignment_of(:attr)
85
+ end
86
+
87
+ def all_protected_attributes
88
+ define_model(:example, attr: :string) do
89
+ attr_accessible nil
90
+ end.new
91
+ end
92
+ end
93
+
94
+ if active_model_3_1?
95
+ context 'an attribute included in the mass-assignment whitelist for admin role only' do
96
+ it 'rejects being mass-assignable' do
97
+ expect(mass_assignable_as_admin).not_to allow_mass_assignment_of(:attr)
98
+ end
99
+
100
+ it 'accepts being mass-assignable for admin' do
101
+ expect(mass_assignable_as_admin).to allow_mass_assignment_of(:attr).as(:admin)
102
+ end
103
+
104
+ def mass_assignable_as_admin
105
+ define_model(:example, attr: :string) do
106
+ attr_accessible :attr, as: :admin
107
+ end.new
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,170 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shoulda::Matchers::ActiveModel::AllowValueMatcher do
4
+ context "#description" do
5
+ it 'describes itself with multiple values' do
6
+ matcher = allow_value('foo', 'bar').for(:baz)
7
+
8
+ expect(matcher.description).to eq 'allow baz to be set to any of ["foo", "bar"]'
9
+ end
10
+
11
+ it 'describes itself with a single value' do
12
+ matcher = allow_value('foo').for(:baz)
13
+
14
+ expect(matcher.description).to eq 'allow baz to be set to "foo"'
15
+ end
16
+
17
+ if active_model_3_2?
18
+ it 'describes itself with a strict validation' do
19
+ strict_matcher = allow_value('xyz').for(:attr).strict
20
+
21
+ expect(strict_matcher.description).
22
+ to eq %q(doesn't raise when attr is set to "xyz")
23
+ end
24
+ end
25
+ end
26
+
27
+ context 'an attribute with a validation' do
28
+ it 'allows a good value' do
29
+ expect(validating_format(with: /abc/)).to allow_value('abcde').for(:attr)
30
+ end
31
+
32
+ it 'rejects a bad value' do
33
+ expect(validating_format(with: /abc/)).not_to allow_value('xyz').for(:attr)
34
+ end
35
+
36
+ it 'allows several good values' do
37
+ expect(validating_format(with: /abc/)).
38
+ to allow_value('abcde', 'deabc').for(:attr)
39
+ end
40
+
41
+ it 'rejects several bad values' do
42
+ expect(validating_format(with: /abc/)).
43
+ not_to allow_value('xyz', 'zyx', nil, []).for(:attr)
44
+ end
45
+ end
46
+
47
+ context 'an attribute with a validation and a custom message' do
48
+ it 'allows a good value' do
49
+ expect(validating_format(with: /abc/, message: 'bad value')).
50
+ to allow_value('abcde').for(:attr).with_message(/bad/)
51
+ end
52
+
53
+ it 'rejects a bad value' do
54
+ expect(validating_format(with: /abc/, message: 'bad value')).
55
+ not_to allow_value('xyz').for(:attr).with_message(/bad/)
56
+ end
57
+ end
58
+
59
+ context 'an attribute where the message occurs on another attribute' do
60
+ it 'allows a good value' do
61
+ expect(record_with_custom_validation).to \
62
+ allow_value('good value').for(:attr).with_message(/some message/, against: :attr2)
63
+ end
64
+
65
+ it 'rejects a bad value' do
66
+ expect(record_with_custom_validation).not_to \
67
+ allow_value('bad value').for(:attr).with_message(/some message/, against: :attr2)
68
+ end
69
+
70
+ def record_with_custom_validation
71
+ define_model :example, attr: :string, attr2: :string do
72
+ validate :custom_validation
73
+
74
+ def custom_validation
75
+ if self[:attr] != 'good value'
76
+ self.errors[:attr2] << 'some message'
77
+ end
78
+ end
79
+ end.new
80
+ end
81
+ end
82
+
83
+ context "an attribute with a context-dependent validation" do
84
+ context "without the validation context" do
85
+ it "allows a bad value" do
86
+ expect(validating_format(with: /abc/, on: :customisable)).to allow_value("xyz").for(:attr)
87
+ end
88
+ end
89
+
90
+ context "with the validation context" do
91
+ it "allows a good value" do
92
+ expect(validating_format(with: /abc/, on: :customisable)).to allow_value("abcde").for(:attr).on(:customisable)
93
+ end
94
+
95
+ it "rejects a bad value" do
96
+ expect(validating_format(with: /abc/, on: :customisable)).not_to allow_value("xyz").for(:attr).on(:customisable)
97
+ end
98
+ end
99
+ end
100
+
101
+ context 'an attribute with several validations' do
102
+ let(:model) do
103
+ define_model :example, attr: :string do
104
+ validates_presence_of :attr
105
+ validates_length_of :attr, within: 1..5
106
+ validates_numericality_of :attr, greater_than_or_equal_to: 1,
107
+ less_than_or_equal_to: 50000
108
+ end.new
109
+ end
110
+ bad_values = [nil, '', 'abc', '0', '50001', '123456', []]
111
+
112
+ it 'allows a good value' do
113
+ expect(model).to allow_value('12345').for(:attr)
114
+ end
115
+
116
+ bad_values.each do |bad_value|
117
+ it "rejects a bad value (#{bad_value.inspect})" do
118
+ expect(model).not_to allow_value(bad_value).for(:attr)
119
+ end
120
+ end
121
+
122
+ it "rejects several bad values (#{bad_values.map(&:inspect).join(', ')})" do
123
+ expect(model).not_to allow_value(*bad_values).for(:attr)
124
+ end
125
+
126
+ it "rejects a mix of both good and bad values" do
127
+ expect(model).not_to allow_value('12345', *bad_values).for(:attr)
128
+ end
129
+ end
130
+
131
+ context 'with a single value' do
132
+ it 'allows you to call description before calling matches?' do
133
+ model = define_model(:example, attr: :string).new
134
+ matcher = described_class.new('foo').for(:attr)
135
+ matcher.description
136
+
137
+ expect { matcher.matches?(model) }.not_to raise_error
138
+ end
139
+ end
140
+
141
+ context 'with no values' do
142
+ it 'raises an error' do
143
+ expect { allow_value.for(:baz) }.
144
+ to raise_error(ArgumentError, /at least one argument/)
145
+ end
146
+ end
147
+
148
+ if active_model_3_2?
149
+ context 'an attribute with a strict format validation' do
150
+ it 'strictly rejects a bad value' do
151
+ expect(validating_format(with: /abc/, strict: true)).
152
+ not_to allow_value('xyz').for(:attr).strict
153
+ end
154
+
155
+ it 'strictly allows a bad value with a different message' do
156
+ expect(validating_format(with: /abc/, strict: true)).
157
+ to allow_value('xyz').for(:attr).with_message(/abc/).strict
158
+ end
159
+
160
+ it 'provides a useful negative failure message' do
161
+ matcher = allow_value('xyz').for(:attr).strict.with_message(/abc/)
162
+
163
+ matcher.matches?(validating_format(with: /abc/, strict: true))
164
+
165
+ expect(matcher.failure_message_when_negated).to eq 'Expected exception to include /abc/ ' +
166
+ 'when attr is set to "xyz", got Attr is invalid'
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shoulda::Matchers::ActiveModel::DisallowValueMatcher do
4
+ context 'an attribute with a format validation' do
5
+ it 'does not match if the value is allowed' do
6
+ expect(validating_format(with: /abc/)).not_to matcher('abcde').for(:attr)
7
+ end
8
+
9
+ it 'matches if the value is not allowed' do
10
+ expect(validating_format(with: /abc/)).to matcher('xyz').for(:attr)
11
+ end
12
+ end
13
+
14
+ context "an attribute with a context-dependent validation" do
15
+ context "without the validation context" do
16
+ it "does not match" do
17
+ expect(validating_format(with: /abc/, on: :customisable)).not_to matcher("xyz").for(:attr)
18
+ end
19
+ end
20
+
21
+ context "with the validation context" do
22
+ it "disallows a bad value" do
23
+ expect(validating_format(with: /abc/, on: :customisable)).to matcher("xyz").for(:attr).on(:customisable)
24
+ end
25
+
26
+ it "does not match a good value" do
27
+ expect(validating_format(with: /abc/, on: :customisable)).not_to matcher("abcde").for(:attr).on(:customisable)
28
+ end
29
+ end
30
+ end
31
+
32
+ context 'an attribute with a format validation and a custom message' do
33
+ it 'does not match if the value and message are both correct' do
34
+ expect(validating_format(with: /abc/, message: 'good message')).
35
+ not_to matcher('abcde').for(:attr).with_message('good message')
36
+ end
37
+
38
+ it "delegates its failure message to its allow matcher's negative failure message" do
39
+ allow_matcher = stub_everything(failure_message_when_negated: 'allow matcher failure')
40
+ Shoulda::Matchers::ActiveModel::AllowValueMatcher.stubs(:new).returns(allow_matcher)
41
+
42
+ matcher = matcher('abcde').for(:attr).with_message('good message')
43
+ matcher.matches?(validating_format(with: /abc/, message: 'good message'))
44
+
45
+ expect(matcher.failure_message).to eq 'allow matcher failure'
46
+ end
47
+
48
+ it 'matches if the message is correct but the value is not' do
49
+ expect(validating_format(with: /abc/, message: 'good message')).
50
+ to matcher('xyz').for(:attr).with_message('good message')
51
+ end
52
+ end
53
+
54
+ context 'an attribute where the message occurs on another attribute' do
55
+ it 'matches if the message is correct but the value is not' do
56
+ expect(record_with_custom_validation).to \
57
+ matcher('bad value').for(:attr).with_message(/some message/, against: :attr2)
58
+ end
59
+
60
+ it 'does not match if the value and message are both correct' do
61
+ expect(record_with_custom_validation).not_to \
62
+ matcher('good value').for(:attr).with_message(/some message/, against: :attr2)
63
+ end
64
+
65
+ def record_with_custom_validation
66
+ define_model :example, attr: :string, attr2: :string do
67
+ validate :custom_validation
68
+
69
+ def custom_validation
70
+ if self[:attr] != 'good value'
71
+ self.errors[:attr2] << 'some message'
72
+ end
73
+ end
74
+ end.new
75
+ end
76
+ end
77
+
78
+ def matcher(value)
79
+ described_class.new(value)
80
+ end
81
+ end
@@ -0,0 +1,95 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shoulda::Matchers::ActiveModel::EnsureExclusionOfMatcher do
4
+ context 'an attribute which must be excluded from a range' do
5
+ it 'accepts ensuring the correct range' do
6
+ expect(validating_exclusion(in: 2..5)).
7
+ to ensure_exclusion_of(:attr).in_range(2..5)
8
+ end
9
+
10
+ it 'rejects ensuring excluded value' do
11
+ expect(validating_exclusion(in: 2..5)).
12
+ not_to ensure_exclusion_of(:attr).in_range(2..6)
13
+ end
14
+
15
+ it 'does not override the default message with a blank' do
16
+ expect(validating_exclusion(in: 2..5)).
17
+ to ensure_exclusion_of(:attr).in_range(2..5).with_message(nil)
18
+ end
19
+ end
20
+
21
+ context 'an attribute which must be excluded from a range with excluded end' do
22
+ it 'accepts ensuring the correct range' do
23
+ expect(validating_exclusion(in: 2...5)).
24
+ to ensure_exclusion_of(:attr).in_range(2...5)
25
+ end
26
+
27
+ it 'rejects ensuring excluded value' do
28
+ expect(validating_exclusion(in: 2...5)).
29
+ not_to ensure_exclusion_of(:attr).in_range(2...4)
30
+ end
31
+ end
32
+
33
+ context 'an attribute with a custom validation message' do
34
+ it 'accepts ensuring the correct range' do
35
+ expect(validating_exclusion(in: 2..4, message: 'not good')).
36
+ to ensure_exclusion_of(:attr).in_range(2..4).with_message(/not good/)
37
+ end
38
+ end
39
+
40
+ context 'an attribute with custom range validations' do
41
+ it 'accepts ensuring the correct range and messages' do
42
+ model = custom_validation do
43
+ if attr >= 2 && attr <= 5
44
+ errors.add(:attr, 'should be out of this range')
45
+ end
46
+ end
47
+
48
+ expect(model).to ensure_exclusion_of(:attr).in_range(2..5).
49
+ with_message(/should be out of this range/)
50
+
51
+ model = custom_validation do
52
+ if attr >= 2 && attr <= 4
53
+ errors.add(:attr, 'should be out of this range')
54
+ end
55
+ end
56
+
57
+ expect(model).to ensure_exclusion_of(:attr).in_range(2...5).
58
+ with_message(/should be out of this range/)
59
+ end
60
+ end
61
+
62
+ context 'an attribute which must be excluded from an array' do
63
+ it 'accepts with correct array' do
64
+ expect(validating_exclusion(in: %w(one two))).
65
+ to ensure_exclusion_of(:attr).in_array(%w(one two))
66
+ end
67
+
68
+ it 'rejects when only part of array matches' do
69
+ expect(validating_exclusion(in: %w(one two))).
70
+ not_to ensure_exclusion_of(:attr).in_array(%w(one wrong_value))
71
+ end
72
+
73
+ it 'rejects when array does not match at all' do
74
+ expect(validating_exclusion(in: %w(one two))).
75
+ not_to ensure_exclusion_of(:attr).in_array(%w(cat dog))
76
+ end
77
+
78
+ it 'has correct description' do
79
+ expect(ensure_exclusion_of(:attr).in_array([true, 'dog']).description).
80
+ to eq 'ensure exclusion of attr in [true, "dog"]'
81
+ end
82
+
83
+ def validating_exclusion(options)
84
+ define_model(:example, attr: :string) do
85
+ validates_exclusion_of :attr, options
86
+ end.new
87
+ end
88
+ end
89
+
90
+ def validating_exclusion(options)
91
+ define_model(:example, attr: :integer) do
92
+ validates_exclusion_of :attr, options
93
+ end.new
94
+ end
95
+ end