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.
- 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
@@ -25,6 +25,9 @@ module Shoulda # :nodoc:
|
|
25
25
|
attr_reader :failure_message, :negative_failure_message
|
26
26
|
|
27
27
|
def to(value)
|
28
|
+
if !value.is_a?(String) && !value.is_a?(Regexp)
|
29
|
+
raise "cannot match against #{value.inspect}"
|
30
|
+
end
|
28
31
|
@value = value
|
29
32
|
self
|
30
33
|
end
|
@@ -134,7 +137,7 @@ module Shoulda # :nodoc:
|
|
134
137
|
|
135
138
|
def pretty_key
|
136
139
|
if @options[:key]
|
137
|
-
"[:#{@key}]"
|
140
|
+
"[:#{@options[:key]}]"
|
138
141
|
else
|
139
142
|
""
|
140
143
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'shoulda/matchers/active_model/helpers'
|
2
2
|
require 'shoulda/matchers/active_model/validation_matcher'
|
3
|
+
require 'shoulda/matchers/active_model/validation_message_finder'
|
4
|
+
require 'shoulda/matchers/active_model/exception_message_finder'
|
3
5
|
require 'shoulda/matchers/active_model/allow_value_matcher'
|
4
6
|
require 'shoulda/matchers/active_model/ensure_length_of_matcher'
|
5
7
|
require 'shoulda/matchers/active_model/ensure_inclusion_of_matcher'
|
@@ -11,6 +13,7 @@ require 'shoulda/matchers/active_model/validate_acceptance_of_matcher'
|
|
11
13
|
require 'shoulda/matchers/active_model/validate_confirmation_of_matcher'
|
12
14
|
require 'shoulda/matchers/active_model/validate_numericality_of_matcher'
|
13
15
|
require 'shoulda/matchers/active_model/allow_mass_assignment_of_matcher'
|
16
|
+
require 'shoulda/matchers/active_model/errors'
|
14
17
|
|
15
18
|
|
16
19
|
module Shoulda
|
@@ -27,8 +30,19 @@ module Shoulda
|
|
27
30
|
# end
|
28
31
|
# it { should allow_value("(123) 456-7890").for(:phone_number) }
|
29
32
|
# it { should_not allow_mass_assignment_of(:password) }
|
33
|
+
# it { should allow_value('Activated', 'Pending').for(:status).strict }
|
34
|
+
# it { should_not allow_value('Amazing').for(:status).strict }
|
30
35
|
# end
|
31
36
|
#
|
37
|
+
# These tests work with the following model:
|
38
|
+
#
|
39
|
+
# class User < ActiveRecord::Base
|
40
|
+
# validates_presence_of :name
|
41
|
+
# validates_presence_of :phone_number
|
42
|
+
# validates_format_of :phone_number, :with => /\\(\\d{3}\\) \\d{3}\\-\\d{4}/
|
43
|
+
# validates_inclusion_of :status, :in => %w(Activated Pending), :strict => true
|
44
|
+
# attr_accessible :name, :phone_number
|
45
|
+
# end
|
32
46
|
module ActiveModel
|
33
47
|
end
|
34
48
|
end
|
@@ -11,6 +11,9 @@ module Shoulda # :nodoc:
|
|
11
11
|
# * <tt>with_message</tt> - value the test expects to find in
|
12
12
|
# <tt>errors.on(:attribute)</tt>. Regexp or string. If omitted,
|
13
13
|
# the test looks for any errors in <tt>errors.on(:attribute)</tt>.
|
14
|
+
# * <tt>strict</tt> - expects the model to raise an exception when the
|
15
|
+
# validation fails rather than adding to the errors collection. Used for
|
16
|
+
# testing `validates!` and the `:strict => true` validation options.
|
14
17
|
#
|
15
18
|
# Example:
|
16
19
|
# it { should_not allow_value('bad').for(:isbn) }
|
@@ -29,6 +32,7 @@ module Shoulda # :nodoc:
|
|
29
32
|
|
30
33
|
def initialize(*values)
|
31
34
|
@values_to_match = values
|
35
|
+
@message_finder_factory = ValidationMessageFinder
|
32
36
|
@options = {}
|
33
37
|
end
|
34
38
|
|
@@ -42,6 +46,11 @@ module Shoulda # :nodoc:
|
|
42
46
|
self
|
43
47
|
end
|
44
48
|
|
49
|
+
def strict
|
50
|
+
@message_finder_factory = ExceptionMessageFinder
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
45
54
|
def matches?(instance)
|
46
55
|
@instance = instance
|
47
56
|
@values_to_match.none? do |value|
|
@@ -60,30 +69,29 @@ module Shoulda # :nodoc:
|
|
60
69
|
end
|
61
70
|
|
62
71
|
def description
|
63
|
-
|
72
|
+
message_finder.allow_description(allowed_values)
|
64
73
|
end
|
65
74
|
|
66
75
|
private
|
67
76
|
|
68
77
|
def errors_match?
|
69
|
-
|
70
|
-
|
78
|
+
has_messages? && errors_for_attribute_match?
|
79
|
+
end
|
80
|
+
|
81
|
+
def has_messages?
|
82
|
+
message_finder.has_messages?
|
83
|
+
end
|
84
|
+
|
85
|
+
def errors_for_attribute_match?
|
86
|
+
if expected_message
|
87
|
+
@matched_error = errors_match_regexp? || errors_match_string?
|
71
88
|
else
|
72
|
-
|
73
|
-
@matched_error = errors_match_regexp? || errors_match_string?
|
74
|
-
else
|
75
|
-
errors_for_attribute.compact.any?
|
76
|
-
end
|
89
|
+
errors_for_attribute.compact.any?
|
77
90
|
end
|
78
91
|
end
|
79
92
|
|
80
93
|
def errors_for_attribute
|
81
|
-
|
82
|
-
errors = @instance.errors[@attribute]
|
83
|
-
else
|
84
|
-
errors = @instance.errors.on(@attribute)
|
85
|
-
end
|
86
|
-
Array.wrap(errors)
|
94
|
+
message_finder.messages
|
87
95
|
end
|
88
96
|
|
89
97
|
def errors_match_regexp?
|
@@ -100,15 +108,15 @@ module Shoulda # :nodoc:
|
|
100
108
|
|
101
109
|
def expectation
|
102
110
|
includes_expected_message = expected_message ? "to include #{expected_message.inspect}" : ''
|
103
|
-
[
|
111
|
+
[error_source, includes_expected_message, "when #{@attribute} is set to #{@value.inspect}"].join(' ')
|
112
|
+
end
|
113
|
+
|
114
|
+
def error_source
|
115
|
+
message_finder.source_description
|
104
116
|
end
|
105
117
|
|
106
118
|
def error_description
|
107
|
-
|
108
|
-
"no errors"
|
109
|
-
else
|
110
|
-
"errors: #{pretty_error_messages(@instance)}"
|
111
|
-
end
|
119
|
+
message_finder.messages_description
|
112
120
|
end
|
113
121
|
|
114
122
|
def allowed_values
|
@@ -122,16 +130,32 @@ module Shoulda # :nodoc:
|
|
122
130
|
def expected_message
|
123
131
|
if @options.key?(:expected_message)
|
124
132
|
if Symbol === @options[:expected_message]
|
125
|
-
|
133
|
+
default_expected_message
|
126
134
|
else
|
127
135
|
@options[:expected_message]
|
128
136
|
end
|
129
137
|
end
|
130
138
|
end
|
131
139
|
|
140
|
+
def default_expected_message
|
141
|
+
message_finder.expected_message_from(default_attribute_message)
|
142
|
+
end
|
143
|
+
|
144
|
+
def default_attribute_message
|
145
|
+
default_error_message(
|
146
|
+
@options[:expected_message],
|
147
|
+
:model_name => model_name,
|
148
|
+
:attribute => @attribute
|
149
|
+
)
|
150
|
+
end
|
151
|
+
|
132
152
|
def model_name
|
133
153
|
@instance.class.to_s.underscore
|
134
154
|
end
|
155
|
+
|
156
|
+
def message_finder
|
157
|
+
@message_finder ||= @message_finder_factory.new(@instance, @attribute)
|
158
|
+
end
|
135
159
|
end
|
136
160
|
end
|
137
161
|
end
|
@@ -5,6 +5,7 @@ module Shoulda # :nodoc:
|
|
5
5
|
# Ensure that the attribute's value is not in the range specified
|
6
6
|
#
|
7
7
|
# Options:
|
8
|
+
# * <tt>in_array</tt> - the array of not allowed values for this attribute
|
8
9
|
# * <tt>in_range</tt> - the range of not allowed values for this attribute
|
9
10
|
# * <tt>with_message</tt> - value the test expects to find in
|
10
11
|
# <tt>errors.on(:attribute)</tt>. Regexp or string. Defaults to the
|
@@ -18,6 +19,10 @@ module Shoulda # :nodoc:
|
|
18
19
|
end
|
19
20
|
|
20
21
|
class EnsureExclusionOfMatcher < ValidationMatcher # :nodoc:
|
22
|
+
def in_array(array)
|
23
|
+
@array = array
|
24
|
+
self
|
25
|
+
end
|
21
26
|
|
22
27
|
def in_range(range)
|
23
28
|
@range = range
|
@@ -32,20 +37,30 @@ module Shoulda # :nodoc:
|
|
32
37
|
end
|
33
38
|
|
34
39
|
def description
|
35
|
-
"ensure exclusion of #{@attribute} in #{
|
40
|
+
"ensure exclusion of #{@attribute} in #{inspect_message}"
|
36
41
|
end
|
37
42
|
|
38
43
|
def matches?(subject)
|
39
44
|
super(subject)
|
40
45
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
46
|
+
if @range
|
47
|
+
allows_lower_value &&
|
48
|
+
disallows_minimum_value &&
|
49
|
+
allows_higher_value &&
|
50
|
+
disallows_maximum_value
|
51
|
+
elsif @array
|
52
|
+
disallows_all_values_in_array?
|
53
|
+
end
|
45
54
|
end
|
46
55
|
|
47
56
|
private
|
48
57
|
|
58
|
+
def disallows_all_values_in_array?
|
59
|
+
@array.all? do |value|
|
60
|
+
disallows_value_of(value, expected_message)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
49
64
|
def allows_lower_value
|
50
65
|
@minimum == 0 || allows_value_of(@minimum - 1, expected_message)
|
51
66
|
end
|
@@ -65,8 +80,15 @@ module Shoulda # :nodoc:
|
|
65
80
|
def expected_message
|
66
81
|
@expected_message || :exclusion
|
67
82
|
end
|
68
|
-
end
|
69
83
|
|
84
|
+
def inspect_message
|
85
|
+
if @range
|
86
|
+
@range.inspect
|
87
|
+
else
|
88
|
+
@array.inspect
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
70
92
|
end
|
71
93
|
end
|
72
94
|
end
|
@@ -5,7 +5,7 @@ module Shoulda # :nodoc:
|
|
5
5
|
# Ensure that the attribute's value is in the range specified
|
6
6
|
#
|
7
7
|
# Options:
|
8
|
-
# * <tt>in_array</tt> - the
|
8
|
+
# * <tt>in_array</tt> - the array of allowed values for this attribute
|
9
9
|
# * <tt>in_range</tt> - the range of allowed values for this attribute
|
10
10
|
# * <tt>with_low_message</tt> - value the test expects to find in
|
11
11
|
# <tt>errors.on(:attribute)</tt>. Regexp or string. Defaults to the
|
@@ -83,7 +83,7 @@ module Shoulda # :nodoc:
|
|
83
83
|
disallows_higher_value &&
|
84
84
|
allows_maximum_value
|
85
85
|
elsif @array
|
86
|
-
if allows_all_values_in_array? && allows_blank_value? && allows_nil_value?
|
86
|
+
if allows_all_values_in_array? && allows_blank_value? && allows_nil_value? && disallows_value_outside_of_array?
|
87
87
|
true
|
88
88
|
else
|
89
89
|
@failure_message = "#{@array} doesn't match array in validation"
|
@@ -96,7 +96,8 @@ module Shoulda # :nodoc:
|
|
96
96
|
|
97
97
|
def allows_blank_value?
|
98
98
|
if @options.key?(:allow_blank)
|
99
|
-
|
99
|
+
blank_values = ['', ' ', "\n", "\r", "\t", "\f"]
|
100
|
+
@options[:allow_blank] == blank_values.all? { |value| allows_value_of(value) }
|
100
101
|
else
|
101
102
|
true
|
102
103
|
end
|
@@ -135,8 +136,25 @@ module Shoulda # :nodoc:
|
|
135
136
|
def allows_maximum_value
|
136
137
|
allows_value_of(@maximum, @high_message)
|
137
138
|
end
|
138
|
-
end
|
139
139
|
|
140
|
+
def disallows_value_outside_of_array?
|
141
|
+
if value_outside_of_array
|
142
|
+
disallows_value_of(value_outside_of_array)
|
143
|
+
else
|
144
|
+
raise CouldNotDetermineValueOutsideOfArray
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def value_outside_of_array
|
149
|
+
found = @array.detect do |item|
|
150
|
+
!@array.include?(item.next)
|
151
|
+
end
|
152
|
+
|
153
|
+
if found
|
154
|
+
found.next
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
140
158
|
end
|
141
159
|
end
|
142
160
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Shoulda
|
2
|
+
module Matchers
|
3
|
+
module ActiveModel
|
4
|
+
|
5
|
+
# Finds message information from exceptions thrown by #valid?
|
6
|
+
class ExceptionMessageFinder
|
7
|
+
def initialize(instance, attribute)
|
8
|
+
@instance = instance
|
9
|
+
@attribute = attribute
|
10
|
+
end
|
11
|
+
|
12
|
+
def allow_description(allowed_values)
|
13
|
+
"doesn't raise when #{@attribute} is set to #{allowed_values}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def messages_description
|
17
|
+
if has_messages?
|
18
|
+
messages.join
|
19
|
+
else
|
20
|
+
'no exception'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def has_messages?
|
25
|
+
messages.any?
|
26
|
+
end
|
27
|
+
|
28
|
+
def messages
|
29
|
+
@messages ||= validate_and_rescue
|
30
|
+
end
|
31
|
+
|
32
|
+
def source_description
|
33
|
+
'exception'
|
34
|
+
end
|
35
|
+
|
36
|
+
def expected_message_from(attribute_message)
|
37
|
+
"#{human_attribute_name} #{attribute_message}"
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def validate_and_rescue
|
43
|
+
@instance.valid?
|
44
|
+
[]
|
45
|
+
rescue ::ActiveModel::StrictValidationFailed => exception
|
46
|
+
[exception.message]
|
47
|
+
end
|
48
|
+
|
49
|
+
def human_attribute_name
|
50
|
+
@instance.class.human_attribute_name(@attribute)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
|
@@ -122,7 +122,7 @@ module Shoulda # :nodoc:
|
|
122
122
|
previous_value = existing.send(scope)
|
123
123
|
|
124
124
|
# Assume the scope is a foreign key if the field is nil
|
125
|
-
previous_value ||=
|
125
|
+
previous_value ||= correct_type_for_column(@subject.class.columns_hash[scope.to_s])
|
126
126
|
|
127
127
|
next_value = if previous_value.respond_to?(:next)
|
128
128
|
previous_value.next
|
@@ -146,6 +146,14 @@ module Shoulda # :nodoc:
|
|
146
146
|
end
|
147
147
|
end
|
148
148
|
|
149
|
+
def correct_type_for_column(column)
|
150
|
+
if column.type == :string
|
151
|
+
'0'
|
152
|
+
else
|
153
|
+
0
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
149
157
|
def class_name
|
150
158
|
@subject.class.name
|
151
159
|
end
|
@@ -6,6 +6,12 @@ module Shoulda # :nodoc:
|
|
6
6
|
|
7
7
|
def initialize(attribute)
|
8
8
|
@attribute = attribute
|
9
|
+
@strict = false
|
10
|
+
end
|
11
|
+
|
12
|
+
def strict
|
13
|
+
@strict = true
|
14
|
+
self
|
9
15
|
end
|
10
16
|
|
11
17
|
def negative_failure_message
|
@@ -20,10 +26,8 @@ module Shoulda # :nodoc:
|
|
20
26
|
private
|
21
27
|
|
22
28
|
def allows_value_of(value, message = nil)
|
23
|
-
allow =
|
24
|
-
|
25
|
-
for(@attribute).
|
26
|
-
with_message(message)
|
29
|
+
allow = allow_value_matcher(value, message)
|
30
|
+
|
27
31
|
if allow.matches?(@subject)
|
28
32
|
@negative_failure_message = allow.failure_message
|
29
33
|
true
|
@@ -34,10 +38,8 @@ module Shoulda # :nodoc:
|
|
34
38
|
end
|
35
39
|
|
36
40
|
def disallows_value_of(value, message = nil)
|
37
|
-
disallow =
|
38
|
-
|
39
|
-
for(@attribute).
|
40
|
-
with_message(message)
|
41
|
+
disallow = allow_value_matcher(value, message)
|
42
|
+
|
41
43
|
if disallow.matches?(@subject)
|
42
44
|
@failure_message = disallow.negative_failure_message
|
43
45
|
false
|
@@ -46,6 +48,23 @@ module Shoulda # :nodoc:
|
|
46
48
|
true
|
47
49
|
end
|
48
50
|
end
|
51
|
+
|
52
|
+
def allow_value_matcher(value, message)
|
53
|
+
matcher = AllowValueMatcher.
|
54
|
+
new(value).
|
55
|
+
for(@attribute).
|
56
|
+
with_message(message)
|
57
|
+
|
58
|
+
if strict?
|
59
|
+
matcher.strict
|
60
|
+
else
|
61
|
+
matcher
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def strict?
|
66
|
+
@strict
|
67
|
+
end
|
49
68
|
end
|
50
69
|
end
|
51
70
|
end
|