shoulda-matchers 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +5 -0
- data/Appraisals +6 -0
- data/Gemfile.lock +15 -15
- data/NEWS.md +20 -1
- data/README.md +6 -1
- data/features/step_definitions/rails_steps.rb +3 -3
- data/gemfiles/3.0.gemfile.lock +23 -27
- data/gemfiles/3.1.gemfile.lock +26 -30
- data/gemfiles/3.2.gemfile +16 -0
- data/gemfiles/3.2.gemfile.lock +157 -0
- data/lib/shoulda/matchers/action_controller/assign_to_matcher.rb +46 -29
- data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +6 -2
- data/lib/shoulda/matchers/action_controller/respond_with_content_type_matcher.rb +12 -7
- data/lib/shoulda/matchers/action_controller/set_the_flash_matcher.rb +28 -15
- data/lib/shoulda/matchers/action_mailer/have_sent_email_matcher.rb +1 -1
- data/lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb +13 -9
- data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +9 -8
- data/lib/shoulda/matchers/active_model/ensure_exclusion_of_matcher.rb +8 -6
- data/lib/shoulda/matchers/active_model/ensure_inclusion_of_matcher.rb +63 -8
- data/lib/shoulda/matchers/active_model/ensure_length_of_matcher.rb +67 -34
- data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +3 -3
- data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +9 -5
- data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +6 -3
- data/lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb +27 -23
- data/lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb +38 -25
- data/lib/shoulda/matchers/active_record/association_matcher.rb +49 -33
- data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +42 -35
- data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +15 -13
- data/lib/shoulda/matchers/active_record/query_the_database_matcher.rb +24 -23
- data/lib/shoulda/matchers/active_record/serialize_matcher.rb +13 -12
- data/lib/shoulda/matchers/version.rb +1 -1
- data/shoulda-matchers.gemspec +1 -1
- data/spec/shoulda/action_controller/assign_to_matcher_spec.rb +5 -3
- data/spec/shoulda/action_mailer/have_sent_email_spec.rb +40 -0
- data/spec/shoulda/active_model/allow_mass_assignment_of_matcher_spec.rb +12 -10
- data/spec/shoulda/active_model/ensure_inclusion_of_matcher_spec.rb +52 -0
- data/spec/shoulda/active_model/helpers_spec.rb +35 -6
- data/spec/shoulda/active_model/validate_presence_of_matcher_spec.rb +0 -1
- data/spec/shoulda/active_model/validate_uniqueness_of_matcher_spec.rb +8 -1
- data/spec/shoulda/active_record/serialize_matcher_spec.rb +1 -1
- data/spec/support/active_model_versions.rb +9 -0
- data/spec/support/model_builder.rb +15 -7
- metadata +123 -128
@@ -24,30 +24,35 @@ module Shoulda # :nodoc:
|
|
24
24
|
|
25
25
|
def initialize(variable)
|
26
26
|
@variable = variable.to_s
|
27
|
-
@
|
27
|
+
@options = {}
|
28
|
+
@options[:check_value] = false
|
28
29
|
end
|
29
30
|
|
30
31
|
def with_kind_of(expected_class)
|
31
|
-
@expected_class = expected_class
|
32
|
+
@options[:expected_class] = expected_class
|
32
33
|
self
|
33
34
|
end
|
34
35
|
|
35
36
|
def with(expected_value = nil, &block)
|
36
|
-
@check_value
|
37
|
-
@expected_value
|
38
|
-
@expectation_block = block
|
37
|
+
@options[:check_value] = true
|
38
|
+
@options[:expected_value] = expected_value
|
39
|
+
@options[:expectation_block] = block
|
39
40
|
self
|
40
41
|
end
|
41
42
|
|
42
43
|
def matches?(controller)
|
43
44
|
@controller = controller
|
44
|
-
|
45
|
-
assigned_value? &&
|
45
|
+
normalize_expected_value!
|
46
|
+
assigned_value? &&
|
47
|
+
kind_of_expected_class? &&
|
48
|
+
equal_to_expected_value?
|
46
49
|
end
|
47
50
|
|
48
51
|
def description
|
49
52
|
description = "assign @#{@variable}"
|
50
|
-
|
53
|
+
if @options.key?(:expected_class)
|
54
|
+
description << " with a kind of #{@options[:expected_class]}"
|
55
|
+
end
|
51
56
|
description
|
52
57
|
end
|
53
58
|
|
@@ -72,33 +77,45 @@ module Shoulda # :nodoc:
|
|
72
77
|
end
|
73
78
|
|
74
79
|
def kind_of_expected_class?
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
80
|
+
if @options.key?(:expected_class)
|
81
|
+
if assigned_value.kind_of?(@options[:expected_class])
|
82
|
+
@negative_failure_message =
|
83
|
+
"Didn't expect action to assign a kind of #{@options[:expected_class]} " <<
|
84
|
+
"for #{@variable}, but got one anyway"
|
85
|
+
true
|
86
|
+
else
|
87
|
+
@failure_message =
|
88
|
+
"Expected action to assign a kind of #{@options[:expected_class]} " <<
|
89
|
+
"for #{@variable}, but got #{assigned_value.inspect} " <<
|
90
|
+
"(#{assigned_value.class.name})"
|
91
|
+
false
|
92
|
+
end
|
81
93
|
else
|
82
|
-
|
83
|
-
"Expected action to assign a kind of #{@expected_class} " <<
|
84
|
-
"for #{@variable}, but got #{assigned_value.inspect} " <<
|
85
|
-
"(#{assigned_value.class.name})"
|
86
|
-
false
|
94
|
+
true
|
87
95
|
end
|
88
96
|
end
|
89
97
|
|
90
98
|
def equal_to_expected_value?
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
99
|
+
if @options[:check_value]
|
100
|
+
if @options[:expected_value] == assigned_value
|
101
|
+
@negative_failure_message =
|
102
|
+
"Didn't expect action to assign #{@options[:expected_value].inspect} " <<
|
103
|
+
"for #{@variable}, but got it anyway"
|
104
|
+
true
|
105
|
+
else
|
106
|
+
@failure_message =
|
107
|
+
"Expected action to assign #{@options[:expected_value].inspect} " <<
|
108
|
+
"for #{@variable}, but got #{assigned_value.inspect}"
|
109
|
+
false
|
110
|
+
end
|
97
111
|
else
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
112
|
+
true
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def normalize_expected_value!
|
117
|
+
if @options[:expectation_block]
|
118
|
+
@options[:expected_value] = @context.instance_eval(&@options[:expectation_block])
|
102
119
|
end
|
103
120
|
end
|
104
121
|
|
@@ -16,7 +16,9 @@ module Shoulda # :nodoc:
|
|
16
16
|
class RenderWithLayoutMatcher # :nodoc:
|
17
17
|
|
18
18
|
def initialize(expected_layout)
|
19
|
-
|
19
|
+
unless expected_layout.nil?
|
20
|
+
@expected_layout = expected_layout.to_s
|
21
|
+
end
|
20
22
|
end
|
21
23
|
|
22
24
|
# Used to provide access to layouts recorded by
|
@@ -64,7 +66,7 @@ module Shoulda # :nodoc:
|
|
64
66
|
end
|
65
67
|
|
66
68
|
def rendered_layouts
|
67
|
-
if recorded_layouts
|
69
|
+
if recorded_layouts.size > 0
|
68
70
|
recorded_layouts.keys.compact.map { |layout| layout.sub(%r{^layouts/}, '') }
|
69
71
|
else
|
70
72
|
layout = @controller.response.layout
|
@@ -79,6 +81,8 @@ module Shoulda # :nodoc:
|
|
79
81
|
def recorded_layouts
|
80
82
|
if @context
|
81
83
|
@context.instance_variable_get('@layouts')
|
84
|
+
else
|
85
|
+
{}
|
82
86
|
end
|
83
87
|
end
|
84
88
|
|
@@ -32,11 +32,7 @@ module Shoulda # :nodoc:
|
|
32
32
|
|
33
33
|
def matches?(controller)
|
34
34
|
@controller = controller
|
35
|
-
|
36
|
-
response_content_type =~ @content_type
|
37
|
-
else
|
38
|
-
response_content_type == @content_type
|
39
|
-
end
|
35
|
+
content_type_matches_regexp? || content_type_matches_string?
|
40
36
|
end
|
41
37
|
|
42
38
|
def failure_message
|
@@ -49,6 +45,16 @@ module Shoulda # :nodoc:
|
|
49
45
|
|
50
46
|
protected
|
51
47
|
|
48
|
+
def content_type_matches_regexp?
|
49
|
+
if @content_type.is_a?(Regexp)
|
50
|
+
response_content_type =~ @content_type
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def content_type_matches_string?
|
55
|
+
response_content_type == @content_type
|
56
|
+
end
|
57
|
+
|
52
58
|
def response_content_type
|
53
59
|
@controller.response.content_type.to_s
|
54
60
|
end
|
@@ -66,8 +72,7 @@ module Shoulda # :nodoc:
|
|
66
72
|
end
|
67
73
|
|
68
74
|
def expectation
|
69
|
-
"content type to be #{@content_type}, "
|
70
|
-
"but was #{response_content_type}"
|
75
|
+
"content type to be #{@content_type}, but was #{response_content_type}"
|
71
76
|
end
|
72
77
|
end
|
73
78
|
end
|
@@ -18,6 +18,10 @@ module Shoulda # :nodoc:
|
|
18
18
|
end
|
19
19
|
|
20
20
|
class SetTheFlashMatcher # :nodoc:
|
21
|
+
def initialize
|
22
|
+
@options = {}
|
23
|
+
end
|
24
|
+
|
21
25
|
attr_reader :failure_message, :negative_failure_message
|
22
26
|
|
23
27
|
def to(value)
|
@@ -26,12 +30,12 @@ module Shoulda # :nodoc:
|
|
26
30
|
end
|
27
31
|
|
28
32
|
def now
|
29
|
-
@now = true
|
33
|
+
@options[:now] = true
|
30
34
|
self
|
31
35
|
end
|
32
36
|
|
33
37
|
def [](key)
|
34
|
-
@key = key
|
38
|
+
@options[:key] = key
|
35
39
|
self
|
36
40
|
end
|
37
41
|
|
@@ -77,8 +81,8 @@ module Shoulda # :nodoc:
|
|
77
81
|
end
|
78
82
|
|
79
83
|
def flash_values
|
80
|
-
if @key
|
81
|
-
[flash.to_hash[@key]]
|
84
|
+
if @options.key?(:key)
|
85
|
+
[flash.to_hash[@options[:key]]]
|
82
86
|
else
|
83
87
|
flash.to_hash.values
|
84
88
|
end
|
@@ -90,13 +94,17 @@ module Shoulda # :nodoc:
|
|
90
94
|
else
|
91
95
|
@flash = @controller.flash.dup
|
92
96
|
@flash.instance_variable_set(:@used, @controller.flash.instance_variable_get(:@used).dup)
|
93
|
-
|
94
|
-
@flash.sweep
|
95
|
-
end
|
97
|
+
sweep_flash_if_necessary
|
96
98
|
@flash
|
97
99
|
end
|
98
100
|
end
|
99
101
|
|
102
|
+
def sweep_flash_if_necessary
|
103
|
+
unless @options[:now]
|
104
|
+
@flash.sweep
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
100
108
|
def expectation
|
101
109
|
expectation = "the #{expected_flash_invocation} to be set"
|
102
110
|
expectation << " to #{@value.inspect}" unless @value.nil?
|
@@ -113,18 +121,23 @@ module Shoulda # :nodoc:
|
|
113
121
|
end
|
114
122
|
|
115
123
|
def expected_flash_invocation
|
116
|
-
|
117
|
-
|
124
|
+
"flash#{pretty_now}#{pretty_key}"
|
125
|
+
end
|
118
126
|
|
119
|
-
|
120
|
-
|
127
|
+
def pretty_now
|
128
|
+
if @options[:now]
|
129
|
+
".now"
|
130
|
+
else
|
131
|
+
""
|
121
132
|
end
|
133
|
+
end
|
122
134
|
|
123
|
-
|
124
|
-
|
135
|
+
def pretty_key
|
136
|
+
if @options[:key]
|
137
|
+
"[:#{@key}]"
|
138
|
+
else
|
139
|
+
""
|
125
140
|
end
|
126
|
-
|
127
|
-
"flash#{now}#{key}"
|
128
141
|
end
|
129
142
|
end
|
130
143
|
end
|
@@ -20,19 +20,19 @@ module Shoulda # :nodoc:
|
|
20
20
|
|
21
21
|
def initialize(attribute)
|
22
22
|
@attribute = attribute.to_s
|
23
|
+
@options = {}
|
23
24
|
end
|
24
25
|
|
25
26
|
def as(role)
|
26
|
-
|
27
|
-
raise "You can specify role only in Rails 3.1 or greater"
|
27
|
+
if active_model_less_than_3_1?
|
28
|
+
raise "You can specify role only in Rails 3.1 or greater"
|
28
29
|
end
|
29
|
-
@role = role
|
30
|
+
@options[:role] = role
|
30
31
|
self
|
31
32
|
end
|
32
33
|
|
33
34
|
def matches?(subject)
|
34
35
|
@subject = subject
|
35
|
-
@role ||= :default
|
36
36
|
if attr_mass_assignable?
|
37
37
|
if whitelisting?
|
38
38
|
@negative_failure_message = "#{@attribute} was made accessible"
|
@@ -62,6 +62,10 @@ module Shoulda # :nodoc:
|
|
62
62
|
|
63
63
|
private
|
64
64
|
|
65
|
+
def role
|
66
|
+
@options[:role] || :default
|
67
|
+
end
|
68
|
+
|
65
69
|
def protected_attributes
|
66
70
|
@protected_attributes ||= (@subject.class.protected_attributes || [])
|
67
71
|
end
|
@@ -79,10 +83,10 @@ module Shoulda # :nodoc:
|
|
79
83
|
end
|
80
84
|
|
81
85
|
def authorizer
|
82
|
-
if
|
83
|
-
@subject.class.active_authorizer[@role]
|
84
|
-
else
|
86
|
+
if active_model_less_than_3_1?
|
85
87
|
@subject.class.active_authorizer
|
88
|
+
else
|
89
|
+
@subject.class.active_authorizer[role]
|
86
90
|
end
|
87
91
|
end
|
88
92
|
|
@@ -90,8 +94,8 @@ module Shoulda # :nodoc:
|
|
90
94
|
@subject.class.name
|
91
95
|
end
|
92
96
|
|
93
|
-
def
|
94
|
-
::ActiveModel::VERSION::
|
97
|
+
def active_model_less_than_3_1?
|
98
|
+
::ActiveModel::VERSION::STRING.to_f < 3.1
|
95
99
|
end
|
96
100
|
end
|
97
101
|
end
|
@@ -29,6 +29,7 @@ module Shoulda # :nodoc:
|
|
29
29
|
|
30
30
|
def initialize(*values)
|
31
31
|
@values_to_match = values
|
32
|
+
@options = {}
|
32
33
|
end
|
33
34
|
|
34
35
|
def for(attribute)
|
@@ -37,7 +38,7 @@ module Shoulda # :nodoc:
|
|
37
38
|
end
|
38
39
|
|
39
40
|
def with_message(message)
|
40
|
-
@expected_message = message
|
41
|
+
@options[:expected_message] = message
|
41
42
|
self
|
42
43
|
end
|
43
44
|
|
@@ -69,7 +70,7 @@ module Shoulda # :nodoc:
|
|
69
70
|
false
|
70
71
|
else
|
71
72
|
if expected_message
|
72
|
-
errors_match_regexp? || errors_match_string?
|
73
|
+
@matched_error = errors_match_regexp? || errors_match_string?
|
73
74
|
else
|
74
75
|
errors_for_attribute.compact.any?
|
75
76
|
end
|
@@ -87,13 +88,13 @@ module Shoulda # :nodoc:
|
|
87
88
|
|
88
89
|
def errors_match_regexp?
|
89
90
|
if Regexp === expected_message
|
90
|
-
|
91
|
+
errors_for_attribute.detect { |e| e =~ expected_message }
|
91
92
|
end
|
92
93
|
end
|
93
94
|
|
94
95
|
def errors_match_string?
|
95
96
|
if errors_for_attribute.include?(expected_message)
|
96
|
-
|
97
|
+
expected_message
|
97
98
|
end
|
98
99
|
end
|
99
100
|
|
@@ -119,11 +120,11 @@ module Shoulda # :nodoc:
|
|
119
120
|
end
|
120
121
|
|
121
122
|
def expected_message
|
122
|
-
if @expected_message
|
123
|
-
if Symbol === @expected_message
|
124
|
-
default_error_message(@expected_message, :model_name => model_name, :attribute => @attribute)
|
123
|
+
if @options.key?(:expected_message)
|
124
|
+
if Symbol === @options[:expected_message]
|
125
|
+
default_error_message(@options[:expected_message], :model_name => model_name, :attribute => @attribute)
|
125
126
|
else
|
126
|
-
@expected_message
|
127
|
+
@options[:expected_message]
|
127
128
|
end
|
128
129
|
end
|
129
130
|
end
|
@@ -38,8 +38,6 @@ module Shoulda # :nodoc:
|
|
38
38
|
def matches?(subject)
|
39
39
|
super(subject)
|
40
40
|
|
41
|
-
@expected_message ||= :exclusion
|
42
|
-
|
43
41
|
allows_lower_value &&
|
44
42
|
disallows_minimum_value &&
|
45
43
|
allows_higher_value &&
|
@@ -49,19 +47,23 @@ module Shoulda # :nodoc:
|
|
49
47
|
private
|
50
48
|
|
51
49
|
def allows_lower_value
|
52
|
-
@minimum == 0 || allows_value_of(@minimum - 1,
|
50
|
+
@minimum == 0 || allows_value_of(@minimum - 1, expected_message)
|
53
51
|
end
|
54
52
|
|
55
53
|
def allows_higher_value
|
56
|
-
allows_value_of(@maximum + 1,
|
54
|
+
allows_value_of(@maximum + 1, expected_message)
|
57
55
|
end
|
58
56
|
|
59
57
|
def disallows_minimum_value
|
60
|
-
disallows_value_of(@minimum,
|
58
|
+
disallows_value_of(@minimum, expected_message)
|
61
59
|
end
|
62
60
|
|
63
61
|
def disallows_maximum_value
|
64
|
-
disallows_value_of(@maximum,
|
62
|
+
disallows_value_of(@maximum, expected_message)
|
63
|
+
end
|
64
|
+
|
65
|
+
def expected_message
|
66
|
+
@expected_message || :exclusion
|
65
67
|
end
|
66
68
|
end
|
67
69
|
|
@@ -5,6 +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 range of allowed values for this attribute
|
8
9
|
# * <tt>in_range</tt> - the range of allowed values for this attribute
|
9
10
|
# * <tt>with_low_message</tt> - value the test expects to find in
|
10
11
|
# <tt>errors.on(:attribute)</tt>. Regexp or string. Defaults to the
|
@@ -21,6 +22,15 @@ module Shoulda # :nodoc:
|
|
21
22
|
end
|
22
23
|
|
23
24
|
class EnsureInclusionOfMatcher < ValidationMatcher # :nodoc:
|
25
|
+
def initialize(attribute)
|
26
|
+
super(attribute)
|
27
|
+
@options = {}
|
28
|
+
end
|
29
|
+
|
30
|
+
def in_array(array)
|
31
|
+
@array = array
|
32
|
+
self
|
33
|
+
end
|
24
34
|
|
25
35
|
def in_range(range)
|
26
36
|
@range = range
|
@@ -29,6 +39,16 @@ module Shoulda # :nodoc:
|
|
29
39
|
self
|
30
40
|
end
|
31
41
|
|
42
|
+
def allow_blank(allow_blank = true)
|
43
|
+
@options[:allow_blank] = allow_blank
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
def allow_nil(allow_nil = true)
|
48
|
+
@options[:allow_nil] = allow_nil
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
32
52
|
def with_message(message)
|
33
53
|
if message
|
34
54
|
@low_message = message
|
@@ -48,23 +68,58 @@ module Shoulda # :nodoc:
|
|
48
68
|
end
|
49
69
|
|
50
70
|
def description
|
51
|
-
"ensure inclusion of #{@attribute} in #{
|
71
|
+
"ensure inclusion of #{@attribute} in #{inspect_message}"
|
52
72
|
end
|
53
73
|
|
54
74
|
def matches?(subject)
|
55
75
|
super(subject)
|
56
76
|
|
57
|
-
@
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
77
|
+
if @range
|
78
|
+
@low_message ||= :inclusion
|
79
|
+
@high_message ||= :inclusion
|
80
|
+
|
81
|
+
disallows_lower_value &&
|
82
|
+
allows_minimum_value &&
|
83
|
+
disallows_higher_value &&
|
84
|
+
allows_maximum_value
|
85
|
+
elsif @array
|
86
|
+
if allows_all_values_in_array? && allows_blank_value? && allows_nil_value?
|
87
|
+
true
|
88
|
+
else
|
89
|
+
@failure_message = "#{@array} doesn't match array in validation"
|
90
|
+
false
|
91
|
+
end
|
92
|
+
end
|
64
93
|
end
|
65
94
|
|
66
95
|
private
|
67
96
|
|
97
|
+
def allows_blank_value?
|
98
|
+
if @options.key?(:allow_blank)
|
99
|
+
@options[:allow_blank] == allows_value_of('')
|
100
|
+
else
|
101
|
+
true
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def allows_nil_value?
|
106
|
+
if @options.key?(:allow_nil)
|
107
|
+
@options[:allow_nil] == allows_value_of(nil)
|
108
|
+
else
|
109
|
+
true
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def inspect_message
|
114
|
+
@range.nil? ? @array.inspect : @range.inspect
|
115
|
+
end
|
116
|
+
|
117
|
+
def allows_all_values_in_array?
|
118
|
+
@array.all? do |value|
|
119
|
+
allows_value_of(value, @low_message)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
68
123
|
def disallows_lower_value
|
69
124
|
@minimum == 0 || disallows_value_of(@minimum - 1, @low_message)
|
70
125
|
end
|