shoulda-matchers 1.0.0.beta1

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 (40) hide show
  1. data/CONTRIBUTION_GUIDELINES.rdoc +10 -0
  2. data/Gemfile +10 -0
  3. data/Gemfile.lock +116 -0
  4. data/MIT-LICENSE +22 -0
  5. data/README.rdoc +70 -0
  6. data/Rakefile +50 -0
  7. data/lib/shoulda-matchers.rb +8 -0
  8. data/lib/shoulda/matchers/action_controller.rb +38 -0
  9. data/lib/shoulda/matchers/action_controller/assign_to_matcher.rb +114 -0
  10. data/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +50 -0
  11. data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +62 -0
  12. data/lib/shoulda/matchers/action_controller/render_template_matcher.rb +54 -0
  13. data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +99 -0
  14. data/lib/shoulda/matchers/action_controller/respond_with_content_type_matcher.rb +74 -0
  15. data/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +85 -0
  16. data/lib/shoulda/matchers/action_controller/route_matcher.rb +93 -0
  17. data/lib/shoulda/matchers/action_controller/set_session_matcher.rb +98 -0
  18. data/lib/shoulda/matchers/action_controller/set_the_flash_matcher.rb +94 -0
  19. data/lib/shoulda/matchers/action_mailer.rb +22 -0
  20. data/lib/shoulda/matchers/action_mailer/have_sent_email.rb +115 -0
  21. data/lib/shoulda/matchers/active_record.rb +42 -0
  22. data/lib/shoulda/matchers/active_record/allow_mass_assignment_of_matcher.rb +83 -0
  23. data/lib/shoulda/matchers/active_record/allow_value_matcher.rb +110 -0
  24. data/lib/shoulda/matchers/active_record/association_matcher.rb +226 -0
  25. data/lib/shoulda/matchers/active_record/ensure_inclusion_of_matcher.rb +87 -0
  26. data/lib/shoulda/matchers/active_record/ensure_length_of_matcher.rb +141 -0
  27. data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +169 -0
  28. data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +112 -0
  29. data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +59 -0
  30. data/lib/shoulda/matchers/active_record/helpers.rb +34 -0
  31. data/lib/shoulda/matchers/active_record/validate_acceptance_of_matcher.rb +41 -0
  32. data/lib/shoulda/matchers/active_record/validate_format_of_matcher.rb +65 -0
  33. data/lib/shoulda/matchers/active_record/validate_numericality_of_matcher.rb +39 -0
  34. data/lib/shoulda/matchers/active_record/validate_presence_of_matcher.rb +60 -0
  35. data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +148 -0
  36. data/lib/shoulda/matchers/active_record/validation_matcher.rb +56 -0
  37. data/lib/shoulda/matchers/integrations/rspec.rb +23 -0
  38. data/lib/shoulda/matchers/integrations/test_unit.rb +41 -0
  39. data/lib/shoulda/matchers/version.rb +5 -0
  40. metadata +113 -0
@@ -0,0 +1,50 @@
1
+ module Shoulda # :nodoc:
2
+ module Matchers
3
+ module ActionController # :nodoc:
4
+
5
+ # Ensures that filter_parameter_logging is set for the specified key.
6
+ #
7
+ # Example:
8
+ #
9
+ # it { should filter_param(:password) }
10
+ def filter_param(key)
11
+ FilterParamMatcher.new(key)
12
+ end
13
+
14
+ class FilterParamMatcher # :nodoc:
15
+
16
+ def initialize(key)
17
+ @key = key.to_s
18
+ end
19
+
20
+ def matches?(controller)
21
+ @controller = controller
22
+ filters_key?
23
+ end
24
+
25
+ def failure_message
26
+ "Expected #{@key} to be filtered; filtered keys: #{filtered_keys.join(', ')}"
27
+ end
28
+
29
+ def negative_failure_message
30
+ "Did not expect #{@key} to be filtered"
31
+ end
32
+
33
+ def description
34
+ "filter #{@key}"
35
+ end
36
+
37
+ private
38
+
39
+ def filters_key?
40
+ filtered_keys.include?(@key)
41
+ end
42
+
43
+ def filtered_keys
44
+ Rails.application.config.filter_parameters.map { |filter| filter.to_s }
45
+ end
46
+ end
47
+
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,62 @@
1
+ module Shoulda # :nodoc:
2
+ module Matchers
3
+ module ActionController # :nodoc:
4
+
5
+ # Ensures a controller redirected to the given url.
6
+ #
7
+ # Example:
8
+ #
9
+ # it { should redirect_to('http://somewhere.com') }
10
+ # it { should redirect_to(users_path) }
11
+ def redirect_to(url_or_description, &block)
12
+ RedirectToMatcher.new(url_or_description, self, &block)
13
+ end
14
+
15
+ class RedirectToMatcher # :nodoc:
16
+
17
+ def initialize(url_or_description, context, &block)
18
+ if block
19
+ @url_block = block
20
+ @location = url_or_description
21
+ else
22
+ @url = url_or_description
23
+ @location = @url
24
+ end
25
+ @context = context
26
+ end
27
+
28
+ def in_context(context)
29
+ @context = context
30
+ self
31
+ end
32
+
33
+ def matches?(controller)
34
+ @controller = controller
35
+ redirects_to_url?
36
+ end
37
+
38
+ attr_reader :failure_message, :negative_failure_message
39
+
40
+ def description
41
+ "redirect to #{@location}"
42
+ end
43
+
44
+ private
45
+
46
+ def redirects_to_url?
47
+ @url = @context.instance_eval(&@url_block) if @url_block
48
+ begin
49
+ @context.send(:assert_redirected_to, @url)
50
+ @negative_failure_message = "Didn't expect to redirect to #{@url}"
51
+ true
52
+ rescue Test::Unit::AssertionFailedError => error
53
+ @failure_message = error.message
54
+ false
55
+ end
56
+ end
57
+
58
+ end
59
+
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,54 @@
1
+ module Shoulda # :nodoc:
2
+ module Matchers
3
+ module ActionController # :nodoc:
4
+
5
+ # Ensures a controller rendered the given template.
6
+ #
7
+ # Example:
8
+ #
9
+ # it { should render_template(:show) }
10
+ def render_template(template)
11
+ RenderTemplateMatcher.new(template, self)
12
+ end
13
+
14
+ class RenderTemplateMatcher # :nodoc:
15
+
16
+ def initialize(template, context)
17
+ @template = template.to_s
18
+ @context = context
19
+ end
20
+
21
+ def matches?(controller)
22
+ @controller = controller
23
+ renders_template?
24
+ end
25
+
26
+ attr_reader :failure_message, :negative_failure_message
27
+
28
+ def description
29
+ "render template #{@template}"
30
+ end
31
+
32
+ def in_context(context)
33
+ @context = context
34
+ self
35
+ end
36
+
37
+ private
38
+
39
+ def renders_template?
40
+ begin
41
+ @context.send(:assert_template, @template)
42
+ @negative_failure_message = "Didn't expect to render #{@template}"
43
+ true
44
+ rescue Test::Unit::AssertionFailedError => error
45
+ @failure_message = error.message
46
+ false
47
+ end
48
+ end
49
+
50
+ end
51
+
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,99 @@
1
+ module Shoulda # :nodoc:
2
+ module Matchers
3
+ module ActionController # :nodoc:
4
+
5
+ # Ensures that the controller rendered with the given layout.
6
+ #
7
+ # Example:
8
+ #
9
+ # it { should render_with_layout }
10
+ # it { should render_with_layout(:special) }
11
+ # it { should_not render_with_layout }
12
+ def render_with_layout(expected_layout = nil)
13
+ RenderWithLayoutMatcher.new(expected_layout).in_context(self)
14
+ end
15
+
16
+ class RenderWithLayoutMatcher # :nodoc:
17
+
18
+ def initialize(expected_layout)
19
+ @expected_layout = expected_layout.to_s unless expected_layout.nil?
20
+ end
21
+
22
+ # Used to provide access to layouts recorded by
23
+ # ActionController::TemplateAssertions in Rails 3
24
+ def in_context(context)
25
+ @context = context
26
+ self
27
+ end
28
+
29
+ def matches?(controller)
30
+ @controller = controller
31
+ rendered_with_layout? && rendered_with_expected_layout?
32
+ end
33
+
34
+ def failure_message
35
+ "Expected #{expectation}, but #{result}"
36
+ end
37
+
38
+ def negative_failure_message
39
+ "Did not expect #{expectation}, but #{result}"
40
+ end
41
+
42
+ def description
43
+ description = "render with "
44
+ if @expected_layout.nil?
45
+ description << "a layout"
46
+ else
47
+ description << "the #{@expected_layout.inspect} layout"
48
+ end
49
+ description
50
+ end
51
+
52
+ private
53
+
54
+ def rendered_with_layout?
55
+ !rendered_layouts.empty?
56
+ end
57
+
58
+ def rendered_with_expected_layout?
59
+ return true if @expected_layout.nil?
60
+ rendered_layouts.include?(@expected_layout)
61
+ end
62
+
63
+ def rendered_layouts
64
+ if recorded_layouts
65
+ recorded_layouts.keys.compact.map { |layout| layout.sub(%r{^layouts/}, '') }
66
+ else
67
+ layout = @controller.response.layout
68
+ if layout.nil?
69
+ []
70
+ else
71
+ [layout.split('/').last]
72
+ end
73
+ end
74
+ end
75
+
76
+ def recorded_layouts
77
+ if @context
78
+ @context.instance_variable_get('@layouts')
79
+ end
80
+ end
81
+
82
+ def expectation
83
+ "to #{description}"
84
+ end
85
+
86
+ def result
87
+ if rendered_with_layout?
88
+ "rendered with " <<
89
+ rendered_layouts.map { |layout| layout.inspect }.join(", ")
90
+ else
91
+ "rendered without a layout"
92
+ end
93
+ end
94
+
95
+ end
96
+
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,74 @@
1
+ module Shoulda # :nodoc:
2
+ module Matchers
3
+ module ActionController # :nodoc:
4
+
5
+ # Ensures a controller responded with expected 'response' content type.
6
+ #
7
+ # You can pass an explicit content type such as 'application/rss+xml'
8
+ # or its symbolic equivalent :rss
9
+ # or a regular expression such as /rss/
10
+ #
11
+ # Example:
12
+ #
13
+ # it { should respond_with_content_type(:xml) }
14
+ # it { should respond_with_content_type(:csv) }
15
+ # it { should respond_with_content_type(:atom) }
16
+ # it { should respond_with_content_type(:yaml) }
17
+ # it { should respond_with_content_type(:text) }
18
+ # it { should respond_with_content_type('application/rss+xml') }
19
+ # it { should respond_with_content_type(/json/) }
20
+ def respond_with_content_type(content_type)
21
+ RespondWithContentTypeMatcher.new(content_type)
22
+ end
23
+
24
+ class RespondWithContentTypeMatcher # :nodoc:
25
+
26
+ def initialize(content_type)
27
+ @content_type = if content_type.is_a?(Symbol)
28
+ lookup_by_extension(content_type)
29
+ else
30
+ content_type
31
+ end
32
+ end
33
+
34
+ def description
35
+ "respond with content type of #{@content_type}"
36
+ end
37
+
38
+ def matches?(controller)
39
+ @controller = controller
40
+ if @content_type.is_a?(Regexp)
41
+ response_content_type =~ @content_type
42
+ else
43
+ response_content_type == @content_type
44
+ end
45
+ end
46
+
47
+ def failure_message
48
+ "Expected #{expectation}"
49
+ end
50
+
51
+ def negative_failure_message
52
+ "Did not expect #{expectation}"
53
+ end
54
+
55
+ protected
56
+
57
+ def response_content_type
58
+ @controller.response.content_type.to_s
59
+ end
60
+
61
+ def lookup_by_extension(extension)
62
+ Mime::Type.lookup_by_extension(extension.to_s).to_s
63
+ end
64
+
65
+ def expectation
66
+ "content type to be #{@content_type}, " <<
67
+ "but was #{response_content_type}"
68
+ end
69
+
70
+ end
71
+
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,85 @@
1
+ module Shoulda # :nodoc:
2
+ module Matchers
3
+ module ActionController # :nodoc:
4
+
5
+ # Ensures a controller responded with expected 'response' status code.
6
+ #
7
+ # You can pass an explicit status number like 200, 301, 404, 500
8
+ # or its symbolic equivalent :success, :redirect, :missing, :error.
9
+ # See ActionController::StatusCodes for a full list.
10
+ #
11
+ # Example:
12
+ #
13
+ # it { should respond_with(:success) }
14
+ # it { should respond_with(:redirect) }
15
+ # it { should respond_with(:missing) }
16
+ # it { should respond_with(:error) }
17
+ # it { should respond_with(501) }
18
+ def respond_with(status)
19
+ RespondWithMatcher.new(status)
20
+ end
21
+
22
+ class RespondWithMatcher # :nodoc:
23
+
24
+ def initialize(status)
25
+ @status = symbol_to_status_code(status)
26
+ end
27
+
28
+ def matches?(controller)
29
+ @controller = controller
30
+ correct_status_code? || correct_status_code_range?
31
+ end
32
+
33
+ def failure_message
34
+ "Expected #{expectation}"
35
+ end
36
+
37
+ def negative_failure_message
38
+ "Did not expect #{expectation}"
39
+ end
40
+
41
+ def description
42
+ "respond with #{@status}"
43
+ end
44
+
45
+ protected
46
+
47
+ def correct_status_code?
48
+ response_code == @status
49
+ end
50
+
51
+ def correct_status_code_range?
52
+ @status.is_a?(Range) &&
53
+ @status.include?(response_code)
54
+ end
55
+
56
+ def response_code
57
+ @controller.response.response_code
58
+ end
59
+
60
+ def symbol_to_status_code(potential_symbol)
61
+ case potential_symbol
62
+ when :success then 200
63
+ when :redirect then 300..399
64
+ when :missing then 404
65
+ when :error then 500..599
66
+ when Symbol
67
+ if defined?(::Rack::Utils::SYMBOL_TO_STATUS_CODE)
68
+ ::Rack::Utils::SYMBOL_TO_STATUS_CODE[potential_symbol]
69
+ else
70
+ ::ActionController::Base::SYMBOL_TO_STATUS_CODE[potential_symbol]
71
+ end
72
+ else
73
+ potential_symbol
74
+ end
75
+ end
76
+
77
+ def expectation
78
+ "response to be a #{@status}, but was #{response_code}"
79
+ end
80
+
81
+ end
82
+
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,93 @@
1
+ module Shoulda # :nodoc:
2
+ module Matchers
3
+ module ActionController # :nodoc:
4
+
5
+ # Ensures that requesting +path+ using +method+ routes to +options+.
6
+ #
7
+ # If you don't specify a controller, it will use the controller from the
8
+ # example group.
9
+ #
10
+ # +to_param+ is called on the +options+ given.
11
+ #
12
+ # Examples:
13
+ #
14
+ # it { should route(:get, "/posts").
15
+ # to(:controller => :posts, :action => :index) }
16
+ # it { should route(:get, "/posts/new").to(:action => :new) }
17
+ # it { should route(:post, "/posts").to(:action => :create) }
18
+ # it { should route(:get, "/posts/1").to(:action => :show, :id => 1) }
19
+ # it { should route(:edit, "/posts/1").to(:action => :show, :id => 1) }
20
+ # it { should route(:put, "/posts/1").to(:action => :update, :id => 1) }
21
+ # it { should route(:delete, "/posts/1").
22
+ # to(:action => :destroy, :id => 1) }
23
+ # it { should route(:get, "/users/1/posts/1").
24
+ # to(:action => :show, :id => 1, :user_id => 1) }
25
+ def route(method, path)
26
+ RouteMatcher.new(method, path, self)
27
+ end
28
+
29
+ class RouteMatcher # :nodoc:
30
+
31
+ def initialize(method, path, context)
32
+ @method = method
33
+ @path = path
34
+ @context = context
35
+ end
36
+
37
+ def to(params)
38
+ @params = params
39
+ stringify_params!
40
+ self
41
+ end
42
+
43
+ def in_context(context)
44
+ @context = context
45
+ self
46
+ end
47
+
48
+ def matches?(controller)
49
+ @controller = controller
50
+ guess_controller!
51
+ route_recognized?
52
+ end
53
+
54
+ attr_reader :failure_message, :negative_failure_message
55
+
56
+ def description
57
+ "route #{@method.to_s.upcase} #{@path} to/from #{@params.inspect}"
58
+ end
59
+
60
+ private
61
+
62
+ def guess_controller!
63
+ @params[:controller] ||= @controller.controller_path
64
+ end
65
+
66
+ def stringify_params!
67
+ @params.each do |key, value|
68
+ @params[key] = value.is_a?(Array) ? value.collect {|v| v.to_param } : value.to_param
69
+ end
70
+ end
71
+
72
+ def route_recognized?
73
+ begin
74
+ @context.send(:assert_routing,
75
+ { :method => @method, :path => @path },
76
+ @params)
77
+
78
+ @negative_failure_message = "Didn't expect to #{description}"
79
+ true
80
+ rescue ::ActionController::RoutingError => error
81
+ @failure_message = error.message
82
+ false
83
+ rescue Test::Unit::AssertionFailedError => error
84
+ @failure_message = error.message
85
+ false
86
+ end
87
+ end
88
+
89
+ end
90
+
91
+ end
92
+ end
93
+ end