shoulda 2.9.1 → 2.9.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +27 -5
- data/Rakefile +2 -2
- data/lib/shoulda.rb +1 -1
- data/lib/shoulda/{controller.rb → action_controller.rb} +6 -8
- data/lib/shoulda/{controller → action_controller}/helpers.rb +1 -16
- data/lib/shoulda/action_controller/macros.rb +277 -0
- data/lib/shoulda/action_controller/matchers.rb +37 -0
- data/lib/shoulda/action_controller/matchers/assign_to_matcher.rb +109 -0
- data/lib/shoulda/action_controller/matchers/filter_param_matcher.rb +57 -0
- data/lib/shoulda/action_controller/matchers/render_with_layout_matcher.rb +81 -0
- data/lib/shoulda/action_controller/matchers/respond_with_content_type_matcher.rb +70 -0
- data/lib/shoulda/action_controller/matchers/respond_with_matcher.rb +81 -0
- data/lib/shoulda/action_controller/matchers/route_matcher.rb +93 -0
- data/lib/shoulda/action_controller/matchers/set_session_matcher.rb +83 -0
- data/lib/shoulda/action_controller/matchers/set_the_flash_matcher.rb +85 -0
- data/lib/shoulda/action_view.rb +10 -0
- data/lib/shoulda/action_view/macros.rb +56 -0
- data/lib/shoulda/active_record/macros.rb +8 -13
- data/lib/shoulda/active_record/matchers/validate_uniqueness_of_matcher.rb +1 -1
- data/lib/shoulda/rails.rb +4 -3
- data/lib/shoulda/rspec.rb +7 -5
- data/test/functional/posts_controller_test.rb +28 -22
- data/test/functional/users_controller_test.rb +0 -19
- data/test/matchers/{allow_mass_assignment_of_matcher_test.rb → active_record/allow_mass_assignment_of_matcher_test.rb} +1 -1
- data/test/matchers/{allow_value_matcher_test.rb → active_record/allow_value_matcher_test.rb} +1 -1
- data/test/matchers/{association_matcher_test.rb → active_record/association_matcher_test.rb} +1 -1
- data/test/matchers/{ensure_inclusion_of_matcher_test.rb → active_record/ensure_inclusion_of_matcher_test.rb} +1 -1
- data/test/matchers/{ensure_length_of_matcher_test.rb → active_record/ensure_length_of_matcher_test.rb} +1 -1
- data/test/matchers/{have_db_column_matcher_test.rb → active_record/have_db_column_matcher_test.rb} +1 -1
- data/test/matchers/{have_index_matcher_test.rb → active_record/have_index_matcher_test.rb} +1 -1
- data/test/matchers/{have_named_scope_matcher_test.rb → active_record/have_named_scope_matcher_test.rb} +1 -1
- data/test/matchers/{have_readonly_attributes_matcher_test.rb → active_record/have_readonly_attributes_matcher_test.rb} +1 -1
- data/test/matchers/{validate_acceptance_of_matcher_test.rb → active_record/validate_acceptance_of_matcher_test.rb} +1 -1
- data/test/matchers/{validate_numericality_of_matcher_test.rb → active_record/validate_numericality_of_matcher_test.rb} +1 -1
- data/test/matchers/{validate_presence_of_matcher_test.rb → active_record/validate_presence_of_matcher_test.rb} +1 -1
- data/test/matchers/{validate_uniqueness_of_matcher_test.rb → active_record/validate_uniqueness_of_matcher_test.rb} +8 -2
- data/test/matchers/controller/assign_to_matcher_test.rb +35 -0
- data/test/matchers/controller/filter_param_matcher_test.rb +32 -0
- data/test/matchers/controller/render_with_layout_matcher_test.rb +33 -0
- data/test/matchers/controller/respond_with_content_type_matcher_test.rb +27 -0
- data/test/matchers/controller/respond_with_matcher_test.rb +106 -0
- data/test/matchers/controller/route_matcher_test.rb +58 -0
- data/test/matchers/controller/set_session_matcher_test.rb +27 -0
- data/test/matchers/controller/set_the_flash_matcher.rb +41 -0
- data/test/model_builder.rb +47 -2
- data/test/rails_root/app/models/user.rb +2 -1
- data/test/rails_root/config/environment.rb +1 -1
- data/test/rspec_test.rb +207 -0
- data/test/unit/user_test.rb +10 -1
- metadata +38 -22
- data/lib/shoulda/controller/formats/html.rb +0 -199
- data/lib/shoulda/controller/formats/xml.rb +0 -168
- data/lib/shoulda/controller/macros.rb +0 -336
- data/lib/shoulda/controller/resource_options.rb +0 -233
@@ -0,0 +1,109 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module ActionController # :nodoc:
|
3
|
+
module Matchers
|
4
|
+
|
5
|
+
# Ensures that the controller assigned to the named instance variable.
|
6
|
+
#
|
7
|
+
# Options:
|
8
|
+
# * <tt>with_kind_of</tt> - The expected class of the instance variable
|
9
|
+
# being checked.
|
10
|
+
# * <tt>with</tt> - The value that should be assigned.
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
#
|
14
|
+
# it { should assign_to(:user) }
|
15
|
+
# it { should_not assign_to(:user) }
|
16
|
+
# it { should assign_to(:user).with_kind_of(User) }
|
17
|
+
# it { should assign_to(:user).with(@user) }
|
18
|
+
def assign_to(variable)
|
19
|
+
AssignToMatcher.new(variable)
|
20
|
+
end
|
21
|
+
|
22
|
+
class AssignToMatcher # :nodoc:
|
23
|
+
|
24
|
+
def initialize(variable)
|
25
|
+
@variable = variable.to_s
|
26
|
+
end
|
27
|
+
|
28
|
+
def with_kind_of(expected_class)
|
29
|
+
@expected_class = expected_class
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
def with(expected_value)
|
34
|
+
@expected_value = expected_value
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
def matches?(controller)
|
39
|
+
@controller = controller
|
40
|
+
assigned_value? && kind_of_expected_class? && equal_to_expected_value?
|
41
|
+
end
|
42
|
+
|
43
|
+
attr_reader :failure_message, :negative_failure_message
|
44
|
+
|
45
|
+
def description
|
46
|
+
description = "assign @#{@variable}"
|
47
|
+
description << " with a kind of #{@expected_class}" if @expected_class
|
48
|
+
description
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def assigned_value?
|
54
|
+
if assigned_value.nil?
|
55
|
+
@failure_message =
|
56
|
+
"Expected action to assign a value for @#{@variable}"
|
57
|
+
false
|
58
|
+
else
|
59
|
+
@negative_failure_message =
|
60
|
+
"Didn't expect action to assign a value for @#{@variable}, " <<
|
61
|
+
"but it was assigned to #{assigned_value.inspect}"
|
62
|
+
true
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def kind_of_expected_class?
|
67
|
+
return true unless @expected_class
|
68
|
+
if assigned_value.kind_of?(@expected_class)
|
69
|
+
@negative_failure_message =
|
70
|
+
"Didn't expect action to assign a kind of #{@expected_class} " <<
|
71
|
+
"for #{@variable}, but got one anyway"
|
72
|
+
true
|
73
|
+
else
|
74
|
+
@failure_message =
|
75
|
+
"Expected action to assign a kind of #{@expected_class} " <<
|
76
|
+
"for #{@variable}, but got #{@variable.inspect} " <<
|
77
|
+
"(#{@variable.class.name})"
|
78
|
+
false
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def equal_to_expected_value?
|
83
|
+
return true unless @expected_value
|
84
|
+
if @expected_value == assigned_value
|
85
|
+
@negative_failure_message =
|
86
|
+
"Didn't expect action to assign #{@expected_value.inspect} " <<
|
87
|
+
"for #{@variable}, but got it anyway"
|
88
|
+
true
|
89
|
+
else
|
90
|
+
@failure_message =
|
91
|
+
"Expected action to assign #{@expected_value.inspect} " <<
|
92
|
+
"for #{@variable}, but got #{assigned_value.inspect}"
|
93
|
+
false
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def assigned_value
|
98
|
+
assigns[@variable]
|
99
|
+
end
|
100
|
+
|
101
|
+
def assigns
|
102
|
+
@controller.response.template.assigns
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module ActionController # :nodoc:
|
3
|
+
module Matchers
|
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_params? && filters_key?
|
23
|
+
end
|
24
|
+
|
25
|
+
def failure_message
|
26
|
+
"Expected #{@key} to be filtered"
|
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_params?
|
40
|
+
@controller.respond_to?(:filter_parameters)
|
41
|
+
end
|
42
|
+
|
43
|
+
def filters_key?
|
44
|
+
filtered_value == '[FILTERED]'
|
45
|
+
end
|
46
|
+
|
47
|
+
def filtered_value
|
48
|
+
filtered = @controller.send(:filter_parameters,
|
49
|
+
@key.to_s => @key.to_s)
|
50
|
+
filtered[@key.to_s]
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module ActionController # :nodoc:
|
3
|
+
module Matchers
|
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(layout = nil)
|
13
|
+
RenderWithLayout.new(layout)
|
14
|
+
end
|
15
|
+
|
16
|
+
class RenderWithLayout # :nodoc:
|
17
|
+
|
18
|
+
def initialize(layout)
|
19
|
+
@layout = layout.to_s unless layout.nil?
|
20
|
+
end
|
21
|
+
|
22
|
+
def matches?(controller)
|
23
|
+
@controller = controller
|
24
|
+
rendered_with_layout? && rendered_with_expected_layout?
|
25
|
+
end
|
26
|
+
|
27
|
+
def failure_message
|
28
|
+
"Expected #{expectation}, but #{result}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def negative_failure_message
|
32
|
+
"Did not expect #{expectation}, but #{result}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def description
|
36
|
+
description = "render with "
|
37
|
+
if @layout.nil?
|
38
|
+
description << "a layout"
|
39
|
+
else
|
40
|
+
description << "the #{@layout.inspect} layout"
|
41
|
+
end
|
42
|
+
description
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def rendered_with_layout?
|
48
|
+
!layout.blank?
|
49
|
+
end
|
50
|
+
|
51
|
+
def rendered_with_expected_layout?
|
52
|
+
return true if @layout.nil?
|
53
|
+
layout == @layout
|
54
|
+
end
|
55
|
+
|
56
|
+
def layout
|
57
|
+
layout = @controller.response.layout
|
58
|
+
if layout.nil?
|
59
|
+
nil
|
60
|
+
else
|
61
|
+
layout.split('/').last
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def expectation
|
66
|
+
"to #{description}"
|
67
|
+
end
|
68
|
+
|
69
|
+
def result
|
70
|
+
if rendered_with_layout?
|
71
|
+
"rendered with the #{layout.inspect} layout"
|
72
|
+
else
|
73
|
+
"rendered without a layout"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module ActionController # :nodoc:
|
3
|
+
module Matchers
|
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 matches?(controller)
|
35
|
+
@controller = controller
|
36
|
+
if @content_type.is_a?(Regexp)
|
37
|
+
response_content_type =~ @content_type
|
38
|
+
else
|
39
|
+
response_content_type == @content_type
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def failure_message
|
44
|
+
"Expected #{expectation}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def negative_failure_message
|
48
|
+
"Did not expect #{expectation}"
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
def response_content_type
|
54
|
+
@controller.response.content_type
|
55
|
+
end
|
56
|
+
|
57
|
+
def lookup_by_extension(extension)
|
58
|
+
Mime::Type.lookup_by_extension(extension.to_s).to_s
|
59
|
+
end
|
60
|
+
|
61
|
+
def expectation
|
62
|
+
"content type to be #{@content_type}, " <<
|
63
|
+
"but was #{response_content_type}"
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module ActionController # :nodoc:
|
3
|
+
module Matchers
|
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
|
+
::ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE[potential_symbol]
|
68
|
+
else
|
69
|
+
potential_symbol
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def expectation
|
74
|
+
"response to be a #{@status}, but was #{response_code}"
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module ActionController # :nodoc:
|
3
|
+
module Matchers
|
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
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
def in_context(context)
|
43
|
+
@context = context
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
def matches?(controller)
|
48
|
+
@controller = controller
|
49
|
+
guess_controller!
|
50
|
+
stringify_params!
|
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.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
|