yetanothernguyen-shoulda-matchers 1.0.0.beta3

Sign up to get free protection for your applications and to get access to all the features.
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