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.
- data/.travis.yml +1 -0
- data/Appraisals +3 -3
- data/Gemfile.lock +2 -2
- data/NEWS.md +20 -0
- data/README.md +1 -1
- data/gemfiles/3.0.gemfile +1 -1
- data/gemfiles/3.0.gemfile.lock +27 -27
- data/gemfiles/3.1.gemfile +1 -1
- data/gemfiles/3.1.gemfile.lock +36 -36
- data/gemfiles/3.2.gemfile +1 -1
- data/gemfiles/3.2.gemfile.lock +35 -35
- data/lib/shoulda/matchers/action_controller/set_the_flash_matcher.rb +4 -1
- data/lib/shoulda/matchers/active_model.rb +14 -0
- data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +45 -21
- data/lib/shoulda/matchers/active_model/ensure_exclusion_of_matcher.rb +28 -6
- data/lib/shoulda/matchers/active_model/ensure_inclusion_of_matcher.rb +22 -4
- data/lib/shoulda/matchers/active_model/ensure_length_of_matcher.rb +1 -0
- data/lib/shoulda/matchers/active_model/errors.rb +7 -0
- data/lib/shoulda/matchers/active_model/exception_message_finder.rb +58 -0
- data/lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb +9 -1
- data/lib/shoulda/matchers/active_model/validation_matcher.rb +27 -8
- data/lib/shoulda/matchers/active_model/validation_message_finder.rb +69 -0
- data/lib/shoulda/matchers/active_record/association_matcher.rb +14 -5
- data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +8 -5
- data/lib/shoulda/matchers/version.rb +1 -1
- data/shoulda-matchers.gemspec +1 -1
- data/spec/shoulda/action_controller/set_the_flash_matcher_spec.rb +6 -0
- data/spec/shoulda/active_model/allow_value_matcher_spec.rb +32 -0
- data/spec/shoulda/active_model/ensure_exclusion_of_matcher_spec.rb +23 -1
- data/spec/shoulda/active_model/ensure_inclusion_of_matcher_spec.rb +49 -0
- data/spec/shoulda/active_model/ensure_length_of_matcher_spec.rb +13 -0
- data/spec/shoulda/active_model/exception_message_finder_spec.rb +112 -0
- data/spec/shoulda/active_model/validate_presence_of_matcher_spec.rb +15 -0
- data/spec/shoulda/active_model/validation_message_finder_spec.rb +107 -0
- data/spec/shoulda/active_record/association_matcher_spec.rb +8 -0
- data/spec/shoulda/active_record/have_db_index_matcher_spec.rb +17 -0
- 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
|
-
|
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
|
265
|
-
|
269
|
+
if @options.key?(:foreign_key)
|
270
|
+
reflection.options[:foreign_key] == @options[:foreign_key]
|
266
271
|
else
|
267
|
-
|
268
|
-
|
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
|
-
|
66
|
-
|
67
|
-
|
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
|
-
|
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
|
data/shoulda-matchers.gemspec
CHANGED
@@ -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
|
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
|