shoulda-matchers 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
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