yetanothernguyen-shoulda-matchers 1.0.0.beta3

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.
Files changed (43) hide show
  1. data/Appraisals +12 -0
  2. data/CONTRIBUTION_GUIDELINES.rdoc +10 -0
  3. data/Gemfile +5 -0
  4. data/MIT-LICENSE +22 -0
  5. data/README.rdoc +78 -0
  6. data/Rakefile +55 -0
  7. data/lib/shoulda-matchers.rb +1 -0
  8. data/lib/shoulda/matchers.rb +8 -0
  9. data/lib/shoulda/matchers/action_controller.rb +38 -0
  10. data/lib/shoulda/matchers/action_controller/assign_to_matcher.rb +114 -0
  11. data/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +50 -0
  12. data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +62 -0
  13. data/lib/shoulda/matchers/action_controller/render_template_matcher.rb +54 -0
  14. data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +99 -0
  15. data/lib/shoulda/matchers/action_controller/respond_with_content_type_matcher.rb +74 -0
  16. data/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +85 -0
  17. data/lib/shoulda/matchers/action_controller/route_matcher.rb +93 -0
  18. data/lib/shoulda/matchers/action_controller/set_session_matcher.rb +98 -0
  19. data/lib/shoulda/matchers/action_controller/set_the_flash_matcher.rb +94 -0
  20. data/lib/shoulda/matchers/action_mailer.rb +22 -0
  21. data/lib/shoulda/matchers/action_mailer/have_sent_email.rb +166 -0
  22. data/lib/shoulda/matchers/active_model.rb +33 -0
  23. data/lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb +83 -0
  24. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +110 -0
  25. data/lib/shoulda/matchers/active_model/ensure_inclusion_of_matcher.rb +87 -0
  26. data/lib/shoulda/matchers/active_model/ensure_length_of_matcher.rb +141 -0
  27. data/lib/shoulda/matchers/active_model/helpers.rb +29 -0
  28. data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +41 -0
  29. data/lib/shoulda/matchers/active_model/validate_format_of_matcher.rb +65 -0
  30. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +39 -0
  31. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +61 -0
  32. data/lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb +148 -0
  33. data/lib/shoulda/matchers/active_model/validation_matcher.rb +56 -0
  34. data/lib/shoulda/matchers/active_record.rb +24 -0
  35. data/lib/shoulda/matchers/active_record/association_matcher.rb +226 -0
  36. data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +169 -0
  37. data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +112 -0
  38. data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +59 -0
  39. data/lib/shoulda/matchers/assertion_error.rb +11 -0
  40. data/lib/shoulda/matchers/integrations/rspec.rb +38 -0
  41. data/lib/shoulda/matchers/integrations/test_unit.rb +54 -0
  42. data/lib/shoulda/matchers/version.rb +5 -0
  43. metadata +169 -0
@@ -0,0 +1,94 @@
1
+ module Shoulda # :nodoc:
2
+ module Matchers
3
+ module ActionController # :nodoc:
4
+
5
+ # Ensures that the flash contains the given value. Can be a String, a
6
+ # Regexp, or nil (indicating that the flash should not be set).
7
+ #
8
+ # Example:
9
+ #
10
+ # it { should set_the_flash }
11
+ # it { should set_the_flash.to("Thank you for placing this order.") }
12
+ # it { should set_the_flash.to(/created/i) }
13
+ # it { should set_the_flash.to(/logged in/i).now }
14
+ # it { should_not set_the_flash }
15
+ def set_the_flash
16
+ SetTheFlashMatcher.new
17
+ end
18
+
19
+ class SetTheFlashMatcher # :nodoc:
20
+
21
+ def to(value)
22
+ @value = value
23
+ self
24
+ end
25
+
26
+ def now
27
+ @now = true
28
+ self
29
+ end
30
+
31
+ def matches?(controller)
32
+ @controller = controller
33
+ sets_the_flash? && string_value_matches? && regexp_value_matches?
34
+ end
35
+
36
+ attr_reader :failure_message, :negative_failure_message
37
+
38
+ def description
39
+ description = "set the flash"
40
+ description << " to #{@value.inspect}" unless @value.nil?
41
+ description
42
+ end
43
+
44
+ def failure_message
45
+ "Expected #{expectation}"
46
+ end
47
+
48
+ def negative_failure_message
49
+ "Did not expect #{expectation}"
50
+ end
51
+
52
+ private
53
+
54
+ def sets_the_flash?
55
+ !flash.blank?
56
+ end
57
+
58
+ def string_value_matches?
59
+ return true unless String === @value
60
+ flash.to_hash.values.any? {|value| value == @value }
61
+ end
62
+
63
+ def regexp_value_matches?
64
+ return true unless Regexp === @value
65
+ flash.to_hash.values.any? {|value| value =~ @value }
66
+ end
67
+
68
+ def flash
69
+ return @flash if @flash
70
+ @flash = @controller.flash.dup
71
+ @flash.sweep unless @now
72
+ @flash
73
+ end
74
+
75
+ def expectation
76
+ expectation = "the flash#{".now" if @now} to be set"
77
+ expectation << " to #{@value.inspect}" unless @value.nil?
78
+ expectation << ", but #{flash_description}"
79
+ expectation
80
+ end
81
+
82
+ def flash_description
83
+ if flash.blank?
84
+ "no flash was set"
85
+ else
86
+ "was #{flash.inspect}"
87
+ end
88
+ end
89
+
90
+ end
91
+
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,22 @@
1
+ require 'shoulda/matchers/action_mailer/have_sent_email'
2
+
3
+ module Shoulda
4
+ module Matchers
5
+ # = Matchers for your mailers
6
+ #
7
+ # This matcher will test that email is sent properly
8
+ #
9
+ # describe User do
10
+ # it { should have_sent_email.with_subject(/is spam$/) }
11
+ # it { should have_sent_email.from('do-not-reply@example.com') }
12
+ # it { should have_sent_email.with_body(/is spam\./) }
13
+ # it { should have_sent_email.to('myself@me.com') }
14
+ # it { should have_sent_email.with_subject(/spam/).
15
+ # from('do-not-reply@example.com').
16
+ # with_body(/spam/).
17
+ # to('myself@me.com') }
18
+ # end
19
+ module ActionMailer
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,166 @@
1
+ module Shoulda # :nodoc:
2
+ module Matchers
3
+ module ActionMailer # :nodoc:
4
+
5
+ # The right email is sent.
6
+ #
7
+ # it { should have_sent_email.with_subject(/is spam$/) }
8
+ # it { should have_sent_email.from('do-not-reply@example.com') }
9
+ # it { should have_sent_email.with_body(/is spam\./) }
10
+ # it { should have_sent_email.to('myself@me.com') }
11
+ # it { should have_sent_email.with_part('text/html', /HTML spam/) }
12
+ # it { should have_sent_email.with_subject(/spam/).
13
+ # from('do-not-reply@example.com').
14
+ # with_body(/spam/).
15
+ # to('myself@me.com') }
16
+ def have_sent_email
17
+ HaveSentEmailMatcher.new
18
+ end
19
+
20
+ class HaveSentEmailMatcher # :nodoc:
21
+
22
+ def initialize
23
+ end
24
+
25
+ def with_subject(email_subject)
26
+ @email_subject = email_subject
27
+ self
28
+ end
29
+
30
+ def from(sender)
31
+ @sender = sender
32
+ self
33
+ end
34
+
35
+ def with_body(body)
36
+ @body = body
37
+ self
38
+ end
39
+
40
+ def with_part(content_type, body)
41
+ @parts ||= []
42
+ @parts << [/#{Regexp.escape(content_type)}/, body, content_type]
43
+ self
44
+ end
45
+
46
+ def to(recipient)
47
+ @recipient = recipient
48
+ self
49
+ end
50
+
51
+ def multipart(flag = true)
52
+ @multipart = !!flag
53
+ self
54
+ end
55
+
56
+ def matches?(subject)
57
+ ::ActionMailer::Base.deliveries.each do |mail|
58
+ @subject_failed = !regexp_or_string_match(mail.subject, @email_subject) if @email_subject
59
+ @parts_failed = !parts_match(mail, @parts) if @parts
60
+ @body_failed = !body_match(mail, @body) if @body
61
+ @sender_failed = !regexp_or_string_match_in_array(mail.from, @sender) if @sender
62
+ @recipient_failed = !regexp_or_string_match_in_array(mail.to, @recipient) if @recipient
63
+ @multipart_failed = mail.multipart? != @multipart if defined?(@multipart)
64
+ return true unless anything_failed?
65
+ end
66
+
67
+ false
68
+ end
69
+
70
+ def failure_message
71
+ "Expected #{expectation}"
72
+ end
73
+
74
+ def negative_failure_message
75
+ "Did not expect #{expectation}"
76
+ end
77
+
78
+ def description
79
+ description = "send an email"
80
+ description << " with a subject of #{@email_subject.inspect}" if @email_subject
81
+ description << " containing #{@body.inspect}" if @body
82
+ @parts.each do |_, body, content_type|
83
+ description << " having a #{content_type} part containing #{body.inspect}"
84
+ end if @parts
85
+ description << " from #{@sender.inspect}" if @sender
86
+ description << " to #{@recipient.inspect}" if @recipient
87
+ description
88
+ end
89
+
90
+ private
91
+
92
+ def expectation
93
+ expectation = "sent email"
94
+ expectation << " with subject #{@email_subject.inspect}" if @subject_failed
95
+ expectation << " with body #{@body.inspect}" if @body_failed
96
+ @parts.each do |_, body, content_type|
97
+ expectation << " with a #{content_type} part containing #{body}"
98
+ end if @parts && @parts_failed
99
+ expectation << " from #{@sender.inspect}" if @sender_failed
100
+ expectation << " to #{@recipient.inspect}" if @recipient_failed
101
+ expectation << " #{'not ' if !@multipart}being multipart" if @multipart_failed
102
+ expectation << "\nDeliveries:\n#{inspect_deliveries}"
103
+ end
104
+
105
+ def inspect_deliveries
106
+ ::ActionMailer::Base.deliveries.map do |delivery|
107
+ "#{delivery.subject.inspect} to #{delivery.to.inspect}"
108
+ end.join("\n")
109
+ end
110
+
111
+ def anything_failed?
112
+ @subject_failed || @body_failed || @parts_failed || @sender_failed ||
113
+ @recipient_failed || @multipart_failed
114
+ end
115
+
116
+ def regexp_or_string_match(a_string, a_regexp_or_string)
117
+ case a_regexp_or_string
118
+ when Regexp
119
+ a_string =~ a_regexp_or_string
120
+ when String
121
+ a_string == a_regexp_or_string
122
+ end
123
+ end
124
+
125
+ def regexp_or_string_match_in_array(an_array, a_regexp_or_string)
126
+ case a_regexp_or_string
127
+ when Regexp
128
+ an_array.any? { |string| string =~ a_regexp_or_string }
129
+ when String
130
+ an_array.include?(a_regexp_or_string)
131
+ end
132
+ end
133
+
134
+ def body_match(mail, a_regexp_or_string)
135
+ # Mail objects instantiated by ActionMailer3 return a blank
136
+ # body if the e-mail is multipart. TMail concatenates the
137
+ # String representation of each part instead.
138
+ if mail.body.blank? && mail.multipart?
139
+ part_match(mail, /^text\//, a_regexp_or_string)
140
+ else
141
+ regexp_or_string_match(mail.body, a_regexp_or_string)
142
+ end
143
+ end
144
+
145
+ def parts_match(mail, parts)
146
+ return false if mail.parts.empty?
147
+
148
+ parts.all? do |content_type, match, _|
149
+ part_match(mail, content_type, match)
150
+ end
151
+ end
152
+
153
+ def part_match(mail, content_type, a_regexp_or_string)
154
+ matching = mail.parts.select {|p| p.content_type =~ content_type}
155
+ return false if matching.empty?
156
+
157
+ matching.all? do |part|
158
+ regexp_or_string_match(part.body, a_regexp_or_string)
159
+ end
160
+ end
161
+
162
+ end
163
+ end
164
+ end
165
+
166
+ end
@@ -0,0 +1,33 @@
1
+ require 'shoulda/matchers/active_model/helpers'
2
+ require 'shoulda/matchers/active_model/validation_matcher'
3
+ require 'shoulda/matchers/active_model/allow_value_matcher'
4
+ require 'shoulda/matchers/active_model/ensure_length_of_matcher'
5
+ require 'shoulda/matchers/active_model/ensure_inclusion_of_matcher'
6
+ require 'shoulda/matchers/active_model/validate_presence_of_matcher'
7
+ require 'shoulda/matchers/active_model/validate_format_of_matcher'
8
+ require 'shoulda/matchers/active_model/validate_uniqueness_of_matcher'
9
+ require 'shoulda/matchers/active_model/validate_acceptance_of_matcher'
10
+ require 'shoulda/matchers/active_model/validate_numericality_of_matcher'
11
+ require 'shoulda/matchers/active_model/allow_mass_assignment_of_matcher'
12
+
13
+
14
+ module Shoulda
15
+ module Matchers
16
+ # = Matchers for your active record models
17
+ #
18
+ # These matchers will test most of the validations of ActiveModel::Validations.
19
+ #
20
+ # describe User do
21
+ # it { should validate_presence_of(:name) }
22
+ # it { should validate_presence_of(:phone_number) }
23
+ # %w(abcd 1234).each do |value|
24
+ # it { should_not allow_value(value).for(:phone_number) }
25
+ # end
26
+ # it { should allow_value("(123) 456-7890").for(:phone_number) }
27
+ # it { should_not allow_mass_assignment_of(:password) }
28
+ # end
29
+ #
30
+ module ActiveModel
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,83 @@
1
+ module Shoulda # :nodoc:
2
+ module Matchers
3
+ module ActiveModel # :nodoc:
4
+
5
+ # Ensures that the attribute can be set on mass update.
6
+ #
7
+ # it { should_not allow_mass_assignment_of(:password) }
8
+ # it { should allow_mass_assignment_of(:first_name) }
9
+ #
10
+ def allow_mass_assignment_of(value)
11
+ AllowMassAssignmentOfMatcher.new(value)
12
+ end
13
+
14
+ class AllowMassAssignmentOfMatcher # :nodoc:
15
+
16
+ def initialize(attribute)
17
+ @attribute = attribute.to_s
18
+ end
19
+
20
+ def matches?(subject)
21
+ @subject = subject
22
+ if attr_mass_assignable?
23
+ if whitelisting?
24
+ @negative_failure_message = "#{@attribute} was made accessible"
25
+ else
26
+ if protected_attributes.empty?
27
+ @negative_failure_message = "no attributes were protected"
28
+ else
29
+ @negative_failure_message = "#{class_name} is protecting " <<
30
+ "#{protected_attributes.to_a.to_sentence}, " <<
31
+ "but not #{@attribute}."
32
+ end
33
+ end
34
+ true
35
+ else
36
+ if whitelisting?
37
+ @failure_message =
38
+ "Expected #{@attribute} to be accessible"
39
+ else
40
+ @failure_message =
41
+ "Did not expect #{@attribute} to be protected"
42
+ end
43
+ false
44
+ end
45
+ end
46
+
47
+ attr_reader :failure_message, :negative_failure_message
48
+
49
+ def description
50
+ "allow mass assignment of #{@attribute}"
51
+ end
52
+
53
+ private
54
+
55
+ def protected_attributes
56
+ @protected_attributes ||= (@subject.class.protected_attributes || [])
57
+ end
58
+
59
+ def accessible_attributes
60
+ @accessible_attributes ||= (@subject.class.accessible_attributes || [])
61
+ end
62
+
63
+ def whitelisting?
64
+ !accessible_attributes.empty?
65
+ end
66
+
67
+ def attr_mass_assignable?
68
+ if whitelisting?
69
+ accessible_attributes.include?(@attribute)
70
+ else
71
+ !protected_attributes.include?(@attribute)
72
+ end
73
+ end
74
+
75
+ def class_name
76
+ @subject.class.name
77
+ end
78
+
79
+ end
80
+
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,110 @@
1
+ module Shoulda # :nodoc:
2
+ module Matchers
3
+ module ActiveModel # :nodoc:
4
+
5
+ # Ensures that the attribute can be set to the given value.
6
+ #
7
+ # Options:
8
+ # * <tt>with_message</tt> - value the test expects to find in
9
+ # <tt>errors.on(:attribute)</tt>. Regexp or string. If omitted,
10
+ # the test looks for any errors in <tt>errors.on(:attribute)</tt>.
11
+ #
12
+ # Example:
13
+ # it { should_not allow_value('bad').for(:isbn) }
14
+ # it { should allow_value("isbn 1 2345 6789 0").for(:isbn) }
15
+ #
16
+ def allow_value(value)
17
+ AllowValueMatcher.new(value)
18
+ end
19
+
20
+ class AllowValueMatcher # :nodoc:
21
+ include Helpers
22
+
23
+ def initialize(value)
24
+ @value = value
25
+ end
26
+
27
+ def for(attribute)
28
+ @attribute = attribute
29
+ self
30
+ end
31
+
32
+ def with_message(message)
33
+ @expected_message = message if message
34
+ self
35
+ end
36
+
37
+ def matches?(instance)
38
+ @instance = instance
39
+ if Symbol === @expected_message
40
+ @expected_message = default_error_message(@expected_message)
41
+ end
42
+ @instance.send("#{@attribute}=", @value)
43
+ !errors_match?
44
+ end
45
+
46
+ def failure_message
47
+ "Did not expect #{expectation}, got error: #{@matched_error}"
48
+ end
49
+
50
+ def negative_failure_message
51
+ "Expected #{expectation}, got #{error_description}"
52
+ end
53
+
54
+ def description
55
+ "allow #{@attribute} to be set to #{@value.inspect}"
56
+ end
57
+
58
+ private
59
+
60
+ def errors_match?
61
+ @instance.valid?
62
+ @errors = errors_for_attribute(@instance, @attribute)
63
+ @errors = [@errors] unless @errors.is_a?(Array)
64
+ @expected_message ? (errors_match_regexp? || errors_match_string?) : (@errors.compact.any?)
65
+ end
66
+
67
+ def errors_for_attribute(instance, attribute)
68
+ if instance.errors.respond_to?(:[])
69
+ instance.errors[attribute]
70
+ else
71
+ instance.errors.on(attribute)
72
+ end
73
+ end
74
+
75
+ def errors_match_regexp?
76
+ if Regexp === @expected_message
77
+ @matched_error = @errors.detect { |e| e =~ @expected_message }
78
+ !@matched_error.nil?
79
+ else
80
+ false
81
+ end
82
+ end
83
+
84
+ def errors_match_string?
85
+ if @errors.include?(@expected_message)
86
+ @matched_error = @expected_message
87
+ true
88
+ else
89
+ false
90
+ end
91
+ end
92
+
93
+ def expectation
94
+ "errors " <<
95
+ (@expected_message ? "to include #{@expected_message.inspect} " : "") <<
96
+ "when #{@attribute} is set to #{@value.inspect}"
97
+ end
98
+
99
+ def error_description
100
+ if @instance.errors.empty?
101
+ "no errors"
102
+ else
103
+ "errors: #{pretty_error_messages(@instance)}"
104
+ end
105
+ end
106
+ end
107
+
108
+ end
109
+ end
110
+ end