shoulda-matchers 2.3.0 → 2.4.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Appraisals +12 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +6 -11
- data/NEWS.md +6 -0
- data/features/rails_integration.feature +2 -3
- data/features/step_definitions/rails_steps.rb +40 -8
- data/gemfiles/3.0.gemfile +2 -1
- data/gemfiles/3.0.gemfile.lock +6 -6
- data/gemfiles/3.1.gemfile +2 -1
- data/gemfiles/3.1.gemfile.lock +6 -6
- data/gemfiles/3.2.gemfile +2 -1
- data/gemfiles/3.2.gemfile.lock +6 -6
- data/gemfiles/4.0.gemfile +17 -0
- data/gemfiles/4.0.gemfile.lock +152 -0
- data/lib/shoulda/matchers.rb +1 -0
- data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +1 -1
- data/lib/shoulda/matchers/action_controller/set_the_flash_matcher.rb +24 -10
- data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +13 -7
- data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +2 -2
- data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +18 -6
- data/lib/shoulda/matchers/active_model/validation_matcher.rb +4 -2
- data/lib/shoulda/matchers/active_record.rb +1 -0
- data/lib/shoulda/matchers/active_record/association_matcher.rb +18 -37
- data/lib/shoulda/matchers/active_record/association_matchers/counter_cache_matcher.rb +8 -3
- data/lib/shoulda/matchers/active_record/association_matchers/dependent_matcher.rb +8 -3
- data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +66 -4
- data/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +93 -0
- data/lib/shoulda/matchers/active_record/association_matchers/order_matcher.rb +8 -3
- data/lib/shoulda/matchers/active_record/association_matchers/through_matcher.rb +7 -2
- data/lib/shoulda/matchers/integrations/test_unit.rb +10 -22
- data/lib/shoulda/matchers/rails_shim.rb +41 -0
- data/lib/shoulda/matchers/version.rb +1 -1
- data/shoulda-matchers.gemspec +1 -2
- data/spec/shoulda/matchers/action_controller/render_with_layout_matcher_spec.rb +7 -2
- data/spec/shoulda/matchers/action_controller/route_matcher_spec.rb +2 -2
- data/spec/shoulda/matchers/active_model/allow_mass_assignment_of_matcher_spec.rb +1 -1
- data/spec/shoulda/matchers/active_model/allow_value_matcher_spec.rb +24 -0
- data/spec/shoulda/matchers/active_model/disallow_value_matcher_spec.rb +24 -0
- data/spec/shoulda/matchers/active_model/helpers_spec.rb +5 -7
- data/spec/shoulda/matchers/active_record/association_matcher_spec.rb +46 -12
- data/spec/shoulda/matchers/active_record/have_db_column_matcher_spec.rb +1 -1
- data/spec/support/active_model_versions.rb +6 -6
- metadata +26 -55
@@ -0,0 +1,93 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module Matchers
|
3
|
+
module ActiveRecord # :nodoc:
|
4
|
+
module AssociationMatchers
|
5
|
+
class OptionVerifier
|
6
|
+
delegate :reflection, to: :reflector
|
7
|
+
|
8
|
+
attr_reader :reflector
|
9
|
+
|
10
|
+
RELATION_OPTIONS = [:conditions, :order]
|
11
|
+
|
12
|
+
def initialize(reflector)
|
13
|
+
@reflector = reflector
|
14
|
+
end
|
15
|
+
|
16
|
+
def correct_for_string?(name, expected_value)
|
17
|
+
correct_for?(:string, name, expected_value)
|
18
|
+
end
|
19
|
+
|
20
|
+
def correct_for_boolean?(name, expected_value)
|
21
|
+
correct_for?(:boolean, name, expected_value)
|
22
|
+
end
|
23
|
+
|
24
|
+
def correct_for_hash?(name, expected_value)
|
25
|
+
correct_for?(:hash, name, expected_value)
|
26
|
+
end
|
27
|
+
|
28
|
+
def correct_for_relation_clause?(name, expected_value)
|
29
|
+
correct_for?(:relation_clause, name, expected_value)
|
30
|
+
end
|
31
|
+
|
32
|
+
def actual_value_for(name)
|
33
|
+
if RELATION_OPTIONS.include?(name)
|
34
|
+
actual_value_for_relation_clause(name)
|
35
|
+
else
|
36
|
+
method_name = "actual_value_for_#{name}"
|
37
|
+
if respond_to?(method_name, true)
|
38
|
+
__send__(method_name)
|
39
|
+
else
|
40
|
+
reflection.options[name]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
attr_reader :reflector
|
48
|
+
|
49
|
+
def correct_for?(*args)
|
50
|
+
expected_value, name, type = args.reverse
|
51
|
+
if expected_value.nil?
|
52
|
+
true
|
53
|
+
else
|
54
|
+
expected_value = type_cast(type, expected_value_for(name, expected_value))
|
55
|
+
actual_value = type_cast(type, actual_value_for(name))
|
56
|
+
expected_value == actual_value
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def type_cast(type, value)
|
61
|
+
case type
|
62
|
+
when :string, :relation_clause then value.to_s
|
63
|
+
when :boolean then !!value
|
64
|
+
when :hash then Hash(value).stringify_keys
|
65
|
+
else value
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def expected_value_for(name, value)
|
70
|
+
if RELATION_OPTIONS.include?(name)
|
71
|
+
expected_value_for_relation_clause(name, value)
|
72
|
+
else
|
73
|
+
value
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def expected_value_for_relation_clause(name, value)
|
78
|
+
relation = reflector.build_relation_with_clause(name, value)
|
79
|
+
reflector.extract_relation_clause_from(relation, name)
|
80
|
+
end
|
81
|
+
|
82
|
+
def actual_value_for_relation_clause(name)
|
83
|
+
reflector.extract_relation_clause_from(reflector.association_relation, name)
|
84
|
+
end
|
85
|
+
|
86
|
+
def actual_value_for_class_name
|
87
|
+
reflector.associated_class
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -16,9 +16,9 @@ module Shoulda # :nodoc:
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def matches?(subject)
|
19
|
-
subject = ModelReflector.new(subject, name)
|
19
|
+
self.subject = ModelReflector.new(subject, name)
|
20
20
|
|
21
|
-
if
|
21
|
+
if option_verifier.correct_for_relation_clause?(:order, order)
|
22
22
|
true
|
23
23
|
else
|
24
24
|
self.missing_option = "#{name} should be ordered by #{order}"
|
@@ -27,7 +27,12 @@ module Shoulda # :nodoc:
|
|
27
27
|
end
|
28
28
|
|
29
29
|
private
|
30
|
-
|
30
|
+
|
31
|
+
attr_accessor :subject, :order, :name
|
32
|
+
|
33
|
+
def option_verifier
|
34
|
+
@option_verifier ||= OptionVerifier.new(subject)
|
35
|
+
end
|
31
36
|
end
|
32
37
|
end
|
33
38
|
end
|
@@ -38,18 +38,23 @@ module Shoulda # :nodoc:
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def through_association_correct?
|
41
|
-
if
|
41
|
+
if option_verifier.correct_for_string?(:through, through)
|
42
42
|
true
|
43
43
|
else
|
44
44
|
self.missing_option =
|
45
45
|
"Expected #{name} to have #{name} through #{through}, " +
|
46
|
-
"but got it through #{
|
46
|
+
"but got it through #{option_verifier.actual_value_for(:through)}"
|
47
47
|
false
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
51
|
private
|
52
|
+
|
52
53
|
attr_accessor :through, :name, :subject
|
54
|
+
|
55
|
+
def option_verifier
|
56
|
+
@option_verifier ||= OptionVerifier.new(subject)
|
57
|
+
end
|
53
58
|
end
|
54
59
|
end
|
55
60
|
end
|
@@ -1,10 +1,7 @@
|
|
1
|
-
# :enddoc:
|
2
|
-
require 'test/unit/testcase'
|
3
|
-
|
4
1
|
if defined?(ActionController)
|
5
2
|
require 'shoulda/matchers/action_controller'
|
6
3
|
|
7
|
-
|
4
|
+
ActionController::TestCase.class_eval do
|
8
5
|
include Shoulda::Matchers::ActionController
|
9
6
|
extend Shoulda::Matchers::ActionController
|
10
7
|
|
@@ -16,27 +13,18 @@ end
|
|
16
13
|
|
17
14
|
if defined?(ActiveRecord)
|
18
15
|
require 'shoulda/matchers/active_record'
|
19
|
-
require 'shoulda/matchers/active_model'
|
20
16
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
include Shoulda::Matchers::ActiveRecord
|
25
|
-
extend Shoulda::Matchers::ActiveRecord
|
26
|
-
include Shoulda::Matchers::ActiveModel
|
27
|
-
extend Shoulda::Matchers::ActiveModel
|
28
|
-
end
|
29
|
-
end
|
17
|
+
ActiveSupport::TestCase.class_eval do
|
18
|
+
include Shoulda::Matchers::ActiveRecord
|
19
|
+
extend Shoulda::Matchers::ActiveRecord
|
30
20
|
end
|
31
|
-
|
21
|
+
end
|
22
|
+
|
23
|
+
if defined?(ActiveModel)
|
32
24
|
require 'shoulda/matchers/active_model'
|
33
25
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
include Shoulda::Matchers::ActiveModel
|
38
|
-
extend Shoulda::Matchers::ActiveModel
|
39
|
-
end
|
40
|
-
end
|
26
|
+
ActiveSupport::TestCase.class_eval do
|
27
|
+
include Shoulda::Matchers::ActiveModel
|
28
|
+
extend Shoulda::Matchers::ActiveModel
|
41
29
|
end
|
42
30
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module Matchers
|
3
|
+
class RailsShim # :nodoc:
|
4
|
+
def self.layouts_ivar
|
5
|
+
if rails_major_version >= 4
|
6
|
+
'@_layouts'
|
7
|
+
else
|
8
|
+
'@layouts'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.flashes_ivar
|
13
|
+
if rails_major_version >= 4
|
14
|
+
:@flashes
|
15
|
+
else
|
16
|
+
:@used
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.clean_scope(klass)
|
21
|
+
if rails_major_version == 4
|
22
|
+
klass.all
|
23
|
+
else
|
24
|
+
klass.scoped
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.validates_confirmation_of_error_attribute(matcher)
|
29
|
+
if rails_major_version == 4
|
30
|
+
matcher.confirmation_attribute
|
31
|
+
else
|
32
|
+
matcher.attribute
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.rails_major_version
|
37
|
+
Rails::VERSION::MAJOR
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/shoulda-matchers.gemspec
CHANGED
@@ -28,6 +28,5 @@ Gem::Specification.new do |s|
|
|
28
28
|
s.add_development_dependency('cucumber', '~> 1.1')
|
29
29
|
s.add_development_dependency('rails', '~> 3.0')
|
30
30
|
s.add_development_dependency('rake', '>= 0.9.2')
|
31
|
-
s.add_development_dependency('rspec-rails', '
|
32
|
-
s.add_development_dependency('strong_parameters')
|
31
|
+
s.add_development_dependency('rspec-rails', '>= 2.13.1', '< 3')
|
33
32
|
end
|
@@ -47,8 +47,13 @@ describe Shoulda::Matchers::ActionController::RenderWithLayoutMatcher do
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def set_in_context_layout(layout)
|
50
|
-
|
51
|
-
|
50
|
+
layouts = Hash.new(0)
|
51
|
+
layouts[layout] = 1
|
52
|
+
self.instance_variable_set(layouts_ivar, layouts)
|
53
|
+
end
|
54
|
+
|
55
|
+
def layouts_ivar
|
56
|
+
Shoulda::Matchers::RailsShim.layouts_ivar
|
52
57
|
end
|
53
58
|
|
54
59
|
def controller_without_layout
|
@@ -1,12 +1,12 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe Shoulda::Matchers::ActionController::RouteMatcher do
|
3
|
+
describe Shoulda::Matchers::ActionController::RouteMatcher, type: :controller do
|
4
4
|
context 'given a controller with a defined glob url' do
|
5
5
|
it 'accepts glob route' do
|
6
6
|
controller = define_controller('Examples').new
|
7
7
|
|
8
8
|
define_routes do
|
9
|
-
|
9
|
+
get 'examples/*id', :to => 'examples#example'
|
10
10
|
end
|
11
11
|
|
12
12
|
controller.should route(:get, '/examples/foo/bar').
|
@@ -59,7 +59,7 @@ describe Shoulda::Matchers::ActiveModel::AllowMassAssignmentOfMatcher do
|
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
-
unless active_model_3_2?
|
62
|
+
unless active_model_3_2? || active_model_4_0?
|
63
63
|
context 'an attribute on a class with no protected attributes' do
|
64
64
|
it 'accepts being mass-assignable' do
|
65
65
|
no_protected_attributes.should allow_mass_assignment_of(:attr)
|
@@ -56,6 +56,30 @@ describe Shoulda::Matchers::ActiveModel::AllowValueMatcher do
|
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
+
context 'an attribute where the message occurs on another attribute' do
|
60
|
+
it 'allows a good value' do
|
61
|
+
record_with_custom_validation.should \
|
62
|
+
allow_value('good value').for(:attr).with_message(/some message/, :against => :attr2)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'rejects a bad value' do
|
66
|
+
record_with_custom_validation.should_not \
|
67
|
+
allow_value('bad value').for(:attr).with_message(/some message/, :against => :attr2)
|
68
|
+
end
|
69
|
+
|
70
|
+
def record_with_custom_validation
|
71
|
+
define_model :example, :attr => :string, :attr2 => :string do
|
72
|
+
validate :custom_validation
|
73
|
+
|
74
|
+
def custom_validation
|
75
|
+
if self[:attr] != 'good value'
|
76
|
+
self.errors[:attr2] << 'some message'
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end.new
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
59
83
|
context "an attribute with a context-dependent validation" do
|
60
84
|
context "without the validation context" do
|
61
85
|
it "allows a bad value" do
|
@@ -55,6 +55,30 @@ describe Shoulda::Matchers::ActiveModel::DisallowValueMatcher do
|
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
+
context 'an attribute where the message occurs on another attribute' do
|
59
|
+
it 'matches if the message is correct but the value is not' do
|
60
|
+
record_with_custom_validation.should \
|
61
|
+
matcher('bad value').for(:attr).with_message(/some message/, :against => :attr2)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'does not match if the value and message are both correct' do
|
65
|
+
record_with_custom_validation.should_not \
|
66
|
+
matcher('good value').for(:attr).with_message(/some message/, :against => :attr2)
|
67
|
+
end
|
68
|
+
|
69
|
+
def record_with_custom_validation
|
70
|
+
define_model :example, :attr => :string, :attr2 => :string do
|
71
|
+
validate :custom_validation
|
72
|
+
|
73
|
+
def custom_validation
|
74
|
+
if self[:attr] != 'good value'
|
75
|
+
self.errors[:attr2] << 'some message'
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end.new
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
58
82
|
def matcher(value)
|
59
83
|
described_class.new(value)
|
60
84
|
end
|
@@ -78,13 +78,11 @@ describe Shoulda::Matchers::ActiveModel::Helpers do
|
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
81
|
-
if
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
assert_presence_validation_has_correct_message
|
87
|
-
end
|
81
|
+
context 'if ActiveModel::Errors#generate_message behavior has changed' do
|
82
|
+
it 'provides the right error message for validate_presence_of' do
|
83
|
+
stub_active_model_message_generation(:type => :blank,
|
84
|
+
:message => 'Behavior has diverged.')
|
85
|
+
assert_presence_validation_has_correct_message
|
88
86
|
end
|
89
87
|
end
|
90
88
|
end
|
@@ -67,8 +67,8 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do
|
|
67
67
|
|
68
68
|
it 'accepts an association with a valid :conditions option' do
|
69
69
|
define_model :parent, :adopter => :boolean
|
70
|
-
define_model
|
71
|
-
belongs_to :parent, :
|
70
|
+
define_model(:child, :parent_id => :integer).tap do |model|
|
71
|
+
define_association_with_conditions(model, :belongs_to, :parent, :adopter => true)
|
72
72
|
end
|
73
73
|
|
74
74
|
Child.new.should belong_to(:parent).conditions(:adopter => true)
|
@@ -297,8 +297,8 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do
|
|
297
297
|
|
298
298
|
it 'accepts an association with a valid :conditions option' do
|
299
299
|
define_model :child, :parent_id => :integer, :adopted => :boolean
|
300
|
-
define_model
|
301
|
-
has_many :children, :
|
300
|
+
define_model(:parent).tap do |model|
|
301
|
+
define_association_with_conditions(model, :has_many, :children, :adopted => true)
|
302
302
|
end
|
303
303
|
|
304
304
|
Parent.new.should have_many(:children).conditions(:adopted => true)
|
@@ -374,8 +374,13 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do
|
|
374
374
|
|
375
375
|
def having_many_children(options = {})
|
376
376
|
define_model :child, :parent_id => :integer
|
377
|
-
define_model
|
378
|
-
|
377
|
+
define_model(:parent).tap do |model|
|
378
|
+
if options.key?(:order)
|
379
|
+
order = options.delete(:order)
|
380
|
+
define_association_with_order(model, :has_many, :children, order, options)
|
381
|
+
else
|
382
|
+
model.has_many :children, options
|
383
|
+
end
|
379
384
|
end.new
|
380
385
|
end
|
381
386
|
end
|
@@ -449,8 +454,8 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do
|
|
449
454
|
|
450
455
|
it 'accepts an association with a valid :conditions option' do
|
451
456
|
define_model :detail, :person_id => :integer, :disabled => :boolean
|
452
|
-
define_model
|
453
|
-
has_one :detail, :
|
457
|
+
define_model(:person).tap do |model|
|
458
|
+
define_association_with_conditions(model, :has_one, :detail, :disabled => true)
|
454
459
|
end
|
455
460
|
|
456
461
|
Person.new.should have_one(:detail).conditions(:disabled => true)
|
@@ -524,8 +529,13 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do
|
|
524
529
|
|
525
530
|
def having_one_detail(options = {})
|
526
531
|
define_model :detail, :person_id => :integer
|
527
|
-
define_model
|
528
|
-
|
532
|
+
define_model(:person).tap do |model|
|
533
|
+
if options.key?(:order)
|
534
|
+
order = options.delete(:order)
|
535
|
+
define_association_with_order(model, :has_one, :detail, order, options)
|
536
|
+
else
|
537
|
+
model.has_one :detail, options
|
538
|
+
end
|
529
539
|
end.new
|
530
540
|
end
|
531
541
|
end
|
@@ -565,8 +575,8 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do
|
|
565
575
|
|
566
576
|
it 'accepts an association with a valid :conditions option' do
|
567
577
|
define_model :relative, :adopted => :boolean
|
568
|
-
define_model
|
569
|
-
has_and_belongs_to_many :relatives, :
|
578
|
+
define_model(:person).tap do |model|
|
579
|
+
define_association_with_conditions(model, :has_and_belongs_to_many, :relatives, :adopted => true)
|
570
580
|
end
|
571
581
|
define_model :people_relative, :id => false, :person_id => :integer,
|
572
582
|
:relative_id => :integer
|
@@ -638,4 +648,28 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do
|
|
638
648
|
end.new
|
639
649
|
end
|
640
650
|
end
|
651
|
+
|
652
|
+
def define_association_with_conditions(model, macro, name, conditions, other_options={})
|
653
|
+
args = []
|
654
|
+
options = {}
|
655
|
+
if Shoulda::Matchers::RailsShim.rails_major_version == 4
|
656
|
+
args << lambda { where(conditions) }
|
657
|
+
else
|
658
|
+
options[:conditions] = conditions
|
659
|
+
end
|
660
|
+
args << options
|
661
|
+
model.__send__(macro, name, *args)
|
662
|
+
end
|
663
|
+
|
664
|
+
def define_association_with_order(model, macro, name, order, other_options={})
|
665
|
+
args = []
|
666
|
+
options = {}
|
667
|
+
if Shoulda::Matchers::RailsShim.rails_major_version == 4
|
668
|
+
args << lambda { order(order) }
|
669
|
+
else
|
670
|
+
options[:order] = order
|
671
|
+
end
|
672
|
+
args << options
|
673
|
+
model.__send__(macro, name, *args)
|
674
|
+
end
|
641
675
|
end
|