shoulda-matchers 1.3.0 → 1.4.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 (37) hide show
  1. data/.travis.yml +1 -0
  2. data/Appraisals +3 -3
  3. data/Gemfile.lock +2 -2
  4. data/NEWS.md +20 -0
  5. data/README.md +1 -1
  6. data/gemfiles/3.0.gemfile +1 -1
  7. data/gemfiles/3.0.gemfile.lock +27 -27
  8. data/gemfiles/3.1.gemfile +1 -1
  9. data/gemfiles/3.1.gemfile.lock +36 -36
  10. data/gemfiles/3.2.gemfile +1 -1
  11. data/gemfiles/3.2.gemfile.lock +35 -35
  12. data/lib/shoulda/matchers/action_controller/set_the_flash_matcher.rb +4 -1
  13. data/lib/shoulda/matchers/active_model.rb +14 -0
  14. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +45 -21
  15. data/lib/shoulda/matchers/active_model/ensure_exclusion_of_matcher.rb +28 -6
  16. data/lib/shoulda/matchers/active_model/ensure_inclusion_of_matcher.rb +22 -4
  17. data/lib/shoulda/matchers/active_model/ensure_length_of_matcher.rb +1 -0
  18. data/lib/shoulda/matchers/active_model/errors.rb +7 -0
  19. data/lib/shoulda/matchers/active_model/exception_message_finder.rb +58 -0
  20. data/lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb +9 -1
  21. data/lib/shoulda/matchers/active_model/validation_matcher.rb +27 -8
  22. data/lib/shoulda/matchers/active_model/validation_message_finder.rb +69 -0
  23. data/lib/shoulda/matchers/active_record/association_matcher.rb +14 -5
  24. data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +8 -5
  25. data/lib/shoulda/matchers/version.rb +1 -1
  26. data/shoulda-matchers.gemspec +1 -1
  27. data/spec/shoulda/action_controller/set_the_flash_matcher_spec.rb +6 -0
  28. data/spec/shoulda/active_model/allow_value_matcher_spec.rb +32 -0
  29. data/spec/shoulda/active_model/ensure_exclusion_of_matcher_spec.rb +23 -1
  30. data/spec/shoulda/active_model/ensure_inclusion_of_matcher_spec.rb +49 -0
  31. data/spec/shoulda/active_model/ensure_length_of_matcher_spec.rb +13 -0
  32. data/spec/shoulda/active_model/exception_message_finder_spec.rb +112 -0
  33. data/spec/shoulda/active_model/validate_presence_of_matcher_spec.rb +15 -0
  34. data/spec/shoulda/active_model/validation_message_finder_spec.rb +107 -0
  35. data/spec/shoulda/active_record/association_matcher_spec.rb +8 -0
  36. data/spec/shoulda/active_record/have_db_index_matcher_spec.rb +17 -0
  37. metadata +18 -6
@@ -0,0 +1,69 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveModel
4
+
5
+ # Finds message information from a model's #errors method.
6
+ class ValidationMessageFinder
7
+ include Helpers
8
+
9
+ def initialize(instance, attribute)
10
+ @instance = instance
11
+ @attribute = attribute
12
+ end
13
+
14
+ def allow_description(allowed_values)
15
+ "allow #{@attribute} to be set to #{allowed_values}"
16
+ end
17
+
18
+ def expected_message_from(attribute_message)
19
+ attribute_message
20
+ end
21
+
22
+ def has_messages?
23
+ errors.present?
24
+ end
25
+
26
+ def source_description
27
+ 'errors'
28
+ end
29
+
30
+ def messages_description
31
+ if errors.empty?
32
+ "no errors"
33
+ else
34
+ "errors: #{pretty_error_messages(validated_instance)}"
35
+ end
36
+ end
37
+
38
+ def messages
39
+ Array.wrap(messages_for_attribute)
40
+ end
41
+
42
+ private
43
+
44
+ def messages_for_attribute
45
+ if errors.respond_to?(:[])
46
+ errors[@attribute]
47
+ else
48
+ errors.on(@attribute)
49
+ end
50
+ end
51
+
52
+ def errors
53
+ validated_instance.errors
54
+ end
55
+
56
+ def validated_instance
57
+ @validated_instance ||= validate_instance
58
+ end
59
+
60
+ def validate_instance
61
+ @instance.valid?
62
+ @instance
63
+ end
64
+ end
65
+
66
+ end
67
+ end
68
+ end
69
+
@@ -100,6 +100,11 @@ module Shoulda # :nodoc:
100
100
  self
101
101
  end
102
102
 
103
+ def with_foreign_key(foreign_key)
104
+ @options[:foreign_key] = foreign_key
105
+ self
106
+ end
107
+
103
108
  def validate(validate = true)
104
109
  @validate = validate
105
110
  self
@@ -243,7 +248,7 @@ module Shoulda # :nodoc:
243
248
 
244
249
  def join_table_exists?
245
250
  if @macro != :has_and_belongs_to_many ||
246
- ::ActiveRecord::Base.connection.tables.include?(join_table)
251
+ model_class.connection.tables.include?(join_table)
247
252
  true
248
253
  else
249
254
  @missing = "join table #{join_table} doesn't exist"
@@ -261,11 +266,15 @@ module Shoulda # :nodoc:
261
266
  end
262
267
 
263
268
  def class_has_foreign_key?(klass)
264
- if klass.column_names.include?(foreign_key)
265
- true
269
+ if @options.key?(:foreign_key)
270
+ reflection.options[:foreign_key] == @options[:foreign_key]
266
271
  else
267
- @missing = "#{klass} does not have a #{foreign_key} foreign key."
268
- false
272
+ if klass.column_names.include?(foreign_key)
273
+ true
274
+ else
275
+ @missing = "#{klass} does not have a #{foreign_key} foreign key."
276
+ false
277
+ end
269
278
  end
270
279
  end
271
280
 
@@ -62,13 +62,16 @@ module Shoulda # :nodoc:
62
62
  def correct_unique?
63
63
  return true unless @options.key?(:unique)
64
64
 
65
- if matched_index.unique == @options[:unique]
66
- true
67
- else
65
+ is_unique = matched_index.unique
66
+
67
+ is_unique = !is_unique unless @options[:unique]
68
+
69
+ unless is_unique
68
70
  @missing = "#{table_name} has an index named #{matched_index.name} " <<
69
- "of unique #{matched_index.unique}, not #{@options[:unique]}."
70
- false
71
+ "of unique #{matched_index.unique}, not #{@options[:unique]}."
71
72
  end
73
+
74
+ is_unique
72
75
  end
73
76
 
74
77
  def matched_index
@@ -1,5 +1,5 @@
1
1
  module Shoulda
2
2
  module Matchers
3
- VERSION = '1.3.0'.freeze
3
+ VERSION = '1.4.0'.freeze
4
4
  end
5
5
  end
@@ -22,7 +22,7 @@ Gem::Specification.new do |s|
22
22
  s.add_development_dependency('appraisal', '~> 0.4.0')
23
23
  s.add_development_dependency('aruba')
24
24
  s.add_development_dependency('bourne', '~> 1.1.2')
25
- s.add_development_dependency('bundler', '~> 1.1.0')
25
+ s.add_development_dependency('bundler', '~> 1.1')
26
26
  s.add_development_dependency('cucumber', '~> 1.1.9')
27
27
  s.add_development_dependency('rails', '~> 3.0')
28
28
  s.add_development_dependency('rake', '~> 0.9.2')
@@ -1,6 +1,12 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Shoulda::Matchers::ActionController::SetTheFlashMatcher do
4
+ it "should fail with unmatchable to" do
5
+ expect{
6
+ set_the_flash.to(1).should
7
+ }.to raise_error("cannot match against 1")
8
+ end
9
+
4
10
  context "a controller that sets a flash message" do
5
11
  let(:controller) { build_response { flash[:notice] = 'value' } }
6
12
 
@@ -88,4 +88,36 @@ describe Shoulda::Matchers::ActiveModel::AllowValueMatcher do
88
88
  end.should raise_error(ArgumentError, /at least one argument/)
89
89
  end
90
90
  end
91
+
92
+ if Rails::VERSION::STRING.to_f >= 3.2
93
+ context "an attribute with a strict format validation" do
94
+ let(:model) do
95
+ define_model :example, :attr => :string do
96
+ validates_format_of :attr, :with => /abc/, :strict => true
97
+ end.new
98
+ end
99
+
100
+ it "strictly rejects a bad value" do
101
+ model.should_not allow_value("xyz").for(:attr).strict
102
+ end
103
+
104
+ it "strictly allows a bad value with a different message" do
105
+ model.should allow_value("xyz").for(:attr).with_message(/abc/).strict
106
+ end
107
+
108
+ it "describes itself" do
109
+ allow_value("xyz").for(:attr).strict.description.
110
+ should == %{doesn't raise when attr is set to "xyz"}
111
+ end
112
+
113
+ it "provides a useful negative failure message" do
114
+ matcher = allow_value("xyz").for(:attr).strict.with_message(/abc/)
115
+ matcher.matches?(model)
116
+ matcher.negative_failure_message.
117
+ should == 'Expected exception to include /abc/ ' +
118
+ 'when attr is set to "xyz", got Attr is invalid'
119
+ end
120
+ end
121
+ end
122
+
91
123
  end
@@ -51,7 +51,29 @@ describe Shoulda::Matchers::ActiveModel::EnsureExclusionOfMatcher do
51
51
  it "should accept ensuring the correct range and messages" do
52
52
  @model.should ensure_exclusion_of(:attr).in_range(2..5).with_message(/shoud be out of this range/)
53
53
  end
54
-
55
54
  end
56
55
 
56
+ context "an attribute which must be excluded in an array" do
57
+ before do
58
+ @model = define_model(:example, :attr => :string) do
59
+ validates_exclusion_of :attr, :in => %w(one two)
60
+ end.new
61
+ end
62
+
63
+ it "accepts with correct array" do
64
+ @model.should ensure_exclusion_of(:attr).in_array(%w(one two))
65
+ end
66
+
67
+ it "rejects when only part of array matches" do
68
+ @model.should_not ensure_exclusion_of(:attr).in_array(%w(one wrong_value))
69
+ end
70
+
71
+ it "rejects when array doesn't match at all" do
72
+ @model.should_not ensure_exclusion_of(:attr).in_array(%w(cat dog))
73
+ end
74
+
75
+ it "has correct description" do
76
+ ensure_exclusion_of(:attr).in_array([true, 'dog']).description.should == 'ensure exclusion of attr in [true, "dog"]'
77
+ end
78
+ end
57
79
  end
@@ -1,6 +1,24 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Shoulda::Matchers::ActiveModel::EnsureInclusionOfMatcher do
4
+ context "with no validations" do
5
+ before do
6
+ @model = define_model(:example, :attr => :string) do
7
+ end.new
8
+ end
9
+
10
+ it "should reject an array which does not have a validator defined" do
11
+ @model.should_not ensure_inclusion_of(:attr).in_array(%w(Yes No))
12
+ end
13
+ end
14
+
15
+ context "where we cannot determine a value outside the array" do
16
+ it "should raise a custom exception" do
17
+ @model = define_model(:example, :attr => :string).new
18
+
19
+ expect { @model.should ensure_inclusion_of(:attr).in_array([""]) }.to raise_error Shoulda::Matchers::ActiveModel::CouldNotDetermineValueOutsideOfArray
20
+ end
21
+ end
4
22
 
5
23
  context "an attribute which must be included in a range" do
6
24
  before do
@@ -120,4 +138,35 @@ describe Shoulda::Matchers::ActiveModel::EnsureInclusionOfMatcher do
120
138
  @model.should_not ensure_inclusion_of(:attr).in_array(['one', 'two']).allow_nil(false)
121
139
  end
122
140
  end
141
+
142
+ context "an attribute allowing some blank values but not others" do
143
+ before do
144
+ @model = define_model(:example, :attr => :string) do
145
+ validates_inclusion_of :attr, :in => ['one', 'two', '']
146
+ end.new
147
+ end
148
+
149
+ it "rejects allow_blank" do
150
+ @model.should_not ensure_inclusion_of(:attr).in_array(['one', 'two', '']).allow_blank(true)
151
+ @model.should ensure_inclusion_of(:attr).in_array(['one', 'two', '']).allow_blank(false)
152
+ end
153
+ end
154
+
155
+ if Rails::VERSION::STRING.to_f >= 3.2
156
+ context "a strict attribute which must be included in a range" do
157
+ before do
158
+ @model = define_model(:example, :attr => :integer) do
159
+ validates_inclusion_of :attr, :in => 2..5, :strict => true
160
+ end.new
161
+ end
162
+
163
+ it "should accept ensuring the correct range" do
164
+ @model.should ensure_inclusion_of(:attr).in_range(2..5).strict
165
+ end
166
+
167
+ it "should not accept ensuring another range" do
168
+ @model.should_not ensure_inclusion_of(:attr).in_range(2..6).strict
169
+ end
170
+ end
171
+ end
123
172
  end
@@ -86,6 +86,19 @@ describe Shoulda::Matchers::ActiveModel::EnsureLengthOfMatcher do
86
86
  end
87
87
  end
88
88
 
89
+ context "an attribute with a required exact length and another validation" do
90
+ before do
91
+ @model = define_model(:example, :attr => :string) do
92
+ validates_length_of :attr, :is => 4
93
+ validates_numericality_of :attr
94
+ end.new
95
+ end
96
+
97
+ it "should accept ensuring the correct length" do
98
+ @model.should ensure_length_of(:attr).is_equal_to(4)
99
+ end
100
+ end
101
+
89
102
  context "an attribute with a custom minimum length validation" do
90
103
  before do
91
104
  @model = define_model(:example, :attr => :string) do
@@ -0,0 +1,112 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shoulda::Matchers::ActiveModel::ExceptionMessageFinder do
4
+ if Rails::VERSION::STRING.to_f >= 3.2
5
+ context '#allow_description' do
6
+ it 'describes its attribute' do
7
+ finder = build_finder(:attribute => :attr)
8
+
9
+ description = finder.allow_description('allowed values')
10
+
11
+ description.should == "doesn't raise when attr is set to allowed values"
12
+ end
13
+ end
14
+
15
+ context '#expected_message_from' do
16
+ it 'returns the message with the attribute name prefixed' do
17
+ finder = build_finder(:attribute => :attr)
18
+
19
+ message = finder.expected_message_from('some message')
20
+
21
+ message.should == 'Attr some message'
22
+ end
23
+ end
24
+
25
+ context '#has_messages?' do
26
+ it 'has messages when some validations fail' do
27
+ finder = build_finder(:format => /abc/, :value => 'xyz')
28
+
29
+ result = finder.has_messages?
30
+
31
+ result.should be_true
32
+ end
33
+
34
+ it 'has no messages when all validations pass' do
35
+ finder = build_finder(:format => /abc/, :value => 'abc')
36
+
37
+ result = finder.has_messages?
38
+
39
+ result.should be_false
40
+ end
41
+ end
42
+
43
+ context '#messages' do
44
+ it 'returns errors for the given attribute' do
45
+ finder = build_finder(
46
+ :attribute => :attr,
47
+ :format => /abc/,
48
+ :value => 'xyz'
49
+ )
50
+
51
+ messages = finder.messages
52
+
53
+ messages.should == ['Attr is invalid']
54
+ end
55
+ end
56
+
57
+ context '#messages_description' do
58
+ it 'describes errors for the given attribute' do
59
+ finder = build_finder(
60
+ :attribute => :attr,
61
+ :format => /abc/,
62
+ :value => 'xyz'
63
+ )
64
+
65
+ description = finder.messages_description
66
+
67
+ description.should == 'Attr is invalid'
68
+ end
69
+
70
+ it 'describes errors when there are none' do
71
+ finder = build_finder(:format => /abc/, :value => 'abc')
72
+
73
+ description = finder.messages_description
74
+
75
+ description.should == 'no exception'
76
+ end
77
+ end
78
+
79
+ context '#source_description' do
80
+ it 'describes the source of its messages' do
81
+ finder = build_finder
82
+
83
+ description = finder.source_description
84
+
85
+ description.should == 'exception'
86
+ end
87
+ end
88
+ end
89
+
90
+ def build_finder(arguments = {})
91
+ arguments[:attribute] ||= :attr
92
+ instance = build_instance_validating(
93
+ arguments[:attribute],
94
+ arguments[:format] || /abc/,
95
+ arguments[:value] || 'abc'
96
+ )
97
+ Shoulda::Matchers::ActiveModel::ExceptionMessageFinder.new(
98
+ instance,
99
+ arguments[:attribute]
100
+ )
101
+ end
102
+
103
+ def build_instance_validating(attribute, format, value)
104
+ model_class = define_model(:example, attribute => :string) do
105
+ attr_accessible attribute
106
+ validates_format_of attribute, :with => format, :strict => true
107
+ end
108
+
109
+ model_class.new(attribute => value)
110
+ end
111
+ end
112
+
@@ -117,4 +117,19 @@ describe Shoulda::Matchers::ActiveModel::ValidatePresenceOfMatcher do
117
117
  end
118
118
  end
119
119
 
120
+ if Rails::VERSION::STRING.to_f >= 3.2
121
+ context "a strictly required attribute" do
122
+ before do
123
+ define_model :example, :attr => :string do
124
+ validates_presence_of :attr, :strict => true
125
+ end
126
+ @model = Example.new
127
+ end
128
+
129
+ it "should require a value" do
130
+ @model.should validate_presence_of(:attr).strict
131
+ end
132
+ end
133
+ end
134
+
120
135
  end