benhutton-remarkable_rails 4.0.0.alpha4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,95 @@
1
+ module Remarkable
2
+ module ActionController
3
+ module Matchers
4
+ class AssignToMatcher < Remarkable::ActionController::Base #:nodoc:
5
+ arguments :collection => :names, :as => :name, :block => true
6
+
7
+ optional :with, :block => true
8
+ optional :with_kind_of
9
+
10
+ collection_assertions :assigned_value?, :is_kind_of?, :is_equal_value?
11
+
12
+ before_assert :evaluate_expected_value
13
+
14
+ protected
15
+
16
+ def assigned_value?
17
+ assigns.key?(@name)
18
+ end
19
+
20
+ def is_kind_of?
21
+ return true unless @options[:with_kind_of]
22
+ return assigns[@name].kind_of?(@options[:with_kind_of])
23
+ end
24
+
25
+ # Returns true if :with is not given and no block is given.
26
+ # In case :with is a proc or a block is given, we evaluate it in the
27
+ # @spec scope.
28
+ #
29
+ def is_equal_value?
30
+ return true unless value_to_compare?
31
+ assigns[@name] == @options[:with]
32
+ end
33
+
34
+ def assigns
35
+ #@subject.response.template.assigns.with_indifferent_access
36
+ @subject.instance_variable_get(:@assigns).with_indifferent_access
37
+ end
38
+
39
+ def value_to_compare?
40
+ @options.key?(:with) || @block
41
+ end
42
+
43
+ # Update interpolation options
44
+ def interpolation_options
45
+ if @subject && @subject.response
46
+ { :assign_inspect => assigns[@name].inspect, :assign_class => assigns[@name].class.name }
47
+ else
48
+ { }
49
+ end
50
+ end
51
+
52
+ # Evaluate procs before assert to avoid them appearing in descriptions.
53
+ def evaluate_expected_value
54
+ if value_to_compare?
55
+ value = @options.key?(:with) ? @options[:with] : @block
56
+ value = @spec.instance_eval(&value) if value.is_a?(Proc)
57
+ @options[:with] = value
58
+ end
59
+ end
60
+
61
+ end
62
+
63
+ # Checks if the controller assigned the variables given by name. If you
64
+ # want to check that a variable is not being assigned, please do:
65
+ #
66
+ # should_not_assign_to(:user)
67
+ #
68
+ # If you want to assure that a variable is being assigned to nil, do instead:
69
+ #
70
+ # should_assign_to(:user).with(nil)
71
+ #
72
+ # == Options
73
+ #
74
+ # * <tt>:with</tt> - The value to compare the assign.
75
+ # It can be also be supplied as proc or as a block (see examples below)
76
+ #
77
+ # * <tt>:with_kind_of</tt> - The expected class of the assign.
78
+ #
79
+ # == Examples
80
+ #
81
+ # should_assign_to :user, :with_kind_of => User
82
+ # should_assign_to :user, :with => proc{ users(:first) }
83
+ # should_assign_to(:user){ users(:first) }
84
+ #
85
+ # it { should assign_to(:user) }
86
+ # it { should assign_to(:user, :with => users(:first)) }
87
+ # it { should assign_to(:user, :with_kind_of => User) }
88
+ #
89
+ def assign_to(*args, &block)
90
+ AssignToMatcher.new(*args, &block).spec(self)
91
+ end
92
+
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,41 @@
1
+ module Remarkable
2
+ module ActionController
3
+ module Matchers
4
+ # Do not inherit from ActionController::Base since it don't need all macro stubs behavior.
5
+ class FilterParamsMatcher < Remarkable::Base #:nodoc:
6
+ arguments :collection => :params, :as => :param
7
+
8
+ assertions :respond_to_filter_params?
9
+ collection_assertions :is_filtered?
10
+
11
+ protected
12
+
13
+ def respond_to_filter_params?
14
+ @subject.respond_to?(:filter_parameters)
15
+ end
16
+
17
+ def is_filtered?
18
+ filtered = @subject.send(:filter_parameters, { @param.to_s => @param.to_s })
19
+ filtered[@param.to_s] == '[FILTERED]'
20
+ end
21
+
22
+ end
23
+
24
+ # Checks if the controller filters the given params.
25
+ #
26
+ # == Examples
27
+ #
28
+ # should_filter_params :password
29
+ # should_not_filter_params :username
30
+ #
31
+ # it { should filter_params(:password) }
32
+ # it { should_not filter_params(:username) }
33
+ #
34
+ def filter_params(*params, &block)
35
+ FilterParamsMatcher.new(*params, &block).spec(self)
36
+ end
37
+ alias :filter_param :filter_params
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,118 @@
1
+ module Remarkable
2
+ module ActionController
3
+ module Matchers
4
+ class RedirectToMatcher < Remarkable::ActionController::Base #:nodoc:
5
+
6
+ arguments :expected, :block => true
7
+ optional :with, :block => true
8
+
9
+ assertions :redirected?, :status_matches?, :url_matches?
10
+
11
+ before_assert :evaluate_expected_value
12
+
13
+ before_assert do
14
+ @response = @subject.respond_to?(:response) ? @subject.response : @subject
15
+ @request = @response.instance_variable_get('@request')
16
+ end
17
+
18
+ protected
19
+
20
+ def redirected?
21
+ @response.redirect?
22
+ end
23
+
24
+ def status_matches?
25
+ return true unless @options.key?(:with)
26
+
27
+ actual_status = @response.response_code
28
+ expected_status = @options[:with]
29
+
30
+ return actual_status == expected_status, :status => @response.response_code.inspect
31
+ end
32
+
33
+ def url_matches?
34
+ @actual = @response.redirect_url
35
+
36
+ if @expected.instance_of?(Hash)
37
+ return false unless @actual =~ /^\w+:\/\/#{@request.host}/ && actual_hash
38
+ actual_hash == expected_hash
39
+ else
40
+ @actual == expected_url
41
+ end
42
+ end
43
+
44
+ def actual_hash
45
+ hash_from_url @actual
46
+ end
47
+
48
+ def expected_hash
49
+ hash_from_url expected_url
50
+ end
51
+
52
+ def hash_from_url(url)
53
+ query_hash(url).merge(path_hash(url)).with_indifferent_access
54
+ end
55
+
56
+ def path_hash(url)
57
+ path = url.sub(/^\w+:\/\/#{@request.host}(?::\d+)?/, "").split("?", 2)[0]
58
+ ::Rails.application.routes.recognize_path path, { :method => :get }
59
+ end
60
+
61
+ def query_hash(url)
62
+ query = url.split("?", 2)[1] || ""
63
+
64
+ if defined?(::Rack::Utils)
65
+ ::Rack::Utils.parse_query(query)
66
+ else
67
+ @request.class.parse_query_parameters(query)
68
+ end
69
+ end
70
+
71
+ def expected_url
72
+ case @expected
73
+ when Hash
74
+ return @subject.url_for(@expected)
75
+ when :back
76
+ return @request.env['HTTP_REFERER']
77
+ when %r{^\w+://.*}
78
+ return @expected
79
+ else
80
+ return "http://#{@request.host}" + (@expected.split('')[0] == '/' ? '' : '/') + @expected
81
+ end
82
+ end
83
+
84
+ def interpolation_options
85
+ { :expected => @expected.inspect, :actual => @actual.inspect }
86
+ end
87
+
88
+ def evaluate_expected_value
89
+ @expected ||= @block if @block
90
+ @expected = @spec.instance_eval(&@expected) if @expected.is_a?(Proc)
91
+ end
92
+
93
+ end
94
+
95
+ # Passes if the response redirects to the given url. The url can be a string,
96
+ # a hash or can be supplied as a block (see examples below).
97
+ #
98
+ # == Options
99
+ #
100
+ # * <tt>:with</tt> - The status 30X used when redirecting.
101
+ #
102
+ # == Examples
103
+ #
104
+ # should_redirect_to{ users_url }
105
+ # should_redirect_to(:action => 'index')
106
+ # should_not_redirect_to(:controller => 'users', :action => 'new')
107
+ #
108
+ # it { should redirect_to(users_url).with(302) }
109
+ # it { should redirect_to(:action => 'index') }
110
+ # it { should_not redirect_to(:controller => 'users', :action => 'new') }
111
+ #
112
+ def redirect_to(expected=nil, options={}, &block)
113
+ RedirectToMatcher.new(expected, options, &block).spec(self)
114
+ end
115
+
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,16 @@
1
+ module RSpec::Rails::Matchers
2
+ module RenderTemplate
3
+ extend RSpec::Matchers::DSL
4
+ matcher :render_template do |options, message|
5
+ $matcher_execution_context.send(:run_action!)
6
+ match_unless_raises Test::Unit::AssertionFailedError do |_|
7
+ options = options.to_s if Symbol === options
8
+ assert_template options, message
9
+ end
10
+
11
+ failure_message_for_should do
12
+ rescued_exception.message
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,136 @@
1
+ module Remarkable
2
+ module ActionController
3
+ module Matchers
4
+ class RespondWithMatcher < Remarkable::ActionController::Base #:nodoc:
5
+ arguments :block => true
6
+
7
+ optional :with, :body, :content_type
8
+
9
+ before_assert do
10
+ @response = @subject.respond_to?(:response) ? @subject.response : @subject
11
+ @controller = @spec.instance_variable_get('@controller')
12
+ end
13
+
14
+ before_assert :evaluate_content_type, :evaluate_body
15
+
16
+ assertions :status_matches?, :body_matches?, :content_type_matches?
17
+
18
+ protected
19
+
20
+ def status_matches?
21
+ return true unless @options[:with] # only continue if not nil
22
+
23
+ case @options[:with]
24
+ when :success, :missing, :redirect, :error
25
+ @response.send("#{@options[:with]}?")
26
+ when Fixnum
27
+ @response.response_code == @options[:with]
28
+ when Symbol, String
29
+ @response.response_code == ::ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE[@options[:with].to_sym]
30
+ when Range
31
+ @options[:with].include?(@response.response_code)
32
+ else
33
+ raise ArgumentError, "I don't know how to interpret status #{@options[:with].inspect}, " <<
34
+ "please give me a Fixnum, Symbol, String or Range."
35
+ end
36
+ end
37
+
38
+ def body_matches?
39
+ return true unless @options.key?(:body)
40
+ assert_contains(@response.body, @options[:body])
41
+ end
42
+
43
+ def content_type_matches?
44
+ return true unless @options.key?(:content_type)
45
+ assert_contains(@response.content_type, @options[:content_type])
46
+ end
47
+
48
+ def evaluate_content_type
49
+ return unless @options.key?(:content_type)
50
+
51
+ @options[:content_type] = case @options[:content_type]
52
+ when Symbol
53
+ Mime::Type.lookup_by_extension(@options[:content_type].to_s).to_s
54
+ when Regexp
55
+ @options[:content_type]
56
+ else
57
+ @options[:content_type].to_s
58
+ end
59
+ end
60
+
61
+ def evaluate_body
62
+ if @options.key?(:body) || @block
63
+ value = @options.key?(:body) ? @options[:body] : @block
64
+ value = @spec.instance_eval(&value) if value.is_a?(Proc)
65
+ @options[:body] = value
66
+ end
67
+ end
68
+
69
+ def interpolation_options
70
+ if @response
71
+ { :actual_body => @response.body.inspect,
72
+ :actual_status => @response.response_code.inspect,
73
+ :actual_content_type => @response.content_type.inspect }
74
+ else
75
+ { }
76
+ end
77
+ end
78
+
79
+ end
80
+
81
+ # Passes if the response has the given status. Status can be a Symbol lik
82
+ # :success, :missing, :redirect and :error. Can be also a Fixnum, Range o
83
+ # any other symbol which matches to any of Rails status codes.
84
+ #
85
+ # == Options
86
+ #
87
+ # * <tt>:body</tt> - The body of the response. It accepts strings and or
88
+ # regular expressions. Altought you might be running your tests without
89
+ # integrating your views, this is useful when rendering :xml or :text.
90
+ #
91
+ # * <tt>:content_type</tt> - The content type of the response.
92
+ # It accepts strings ('application/rss+xml'), mime constants (Mime::RSS),
93
+ # symbols (:rss) and regular expressions /rss/.
94
+ #
95
+ # == Examples
96
+ #
97
+ # should_respond_with :success
98
+ # should_respond_with :error, :body => /System error/
99
+ # should_respond_with 301, :content_type => Mime::XML
100
+ # should_respond_with 300..399, :content_type => Mime::XML
101
+ #
102
+ # it { should respond_with(:success) }
103
+ # it { should respond_with(:error).body(/System error/) }
104
+ # it { should respond_with(301).content_type(Mime::XML) }
105
+ # it { should respond_with(300..399).content_type(Mime::XML) }
106
+ #
107
+ def respond_with(*args, &block)
108
+ options = args.extract_options!
109
+ options.merge!(:with => args.first)
110
+ RespondWithMatcher.new(options, &block).spec(self)
111
+ end
112
+
113
+ # This is just a shortcut for respond_with :body => body. Check respond_with
114
+ # for more information.
115
+ #
116
+ def respond_with_body(*args, &block)
117
+ options = args.extract_options!
118
+ # Since body can be also given as block, only merge if any arguments was
119
+ # actually sent.
120
+ options.merge!(:body => args.first) unless args.empty?
121
+ RespondWithMatcher.new(options, &block).spec(self)
122
+ end
123
+
124
+ # This is just a shortcut for respond_with :content_type => content_type.
125
+ # It's also used for Shoulda compatibility. Check respond_with for more
126
+ # information.
127
+ #
128
+ def respond_with_content_type(*args, &block)
129
+ options = args.extract_options!
130
+ options.merge!(:content_type => args.first)
131
+ RespondWithMatcher.new(options, &block).spec(self)
132
+ end
133
+
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,135 @@
1
+ module Remarkable
2
+ module ActionController
3
+ module Matchers
4
+ # Do not inherit from ActionController::Base since it don't need all macro stubs behavior.
5
+ class RouteMatcher < Remarkable::Base #:nodoc:
6
+ arguments :method, :path
7
+ assertions :map_to_path?, :generate_params?
8
+
9
+ # Small hack to allow should route().to/from syntax.
10
+ #
11
+ after_initialize do
12
+ if @path.is_a?(Hash)
13
+ @options.merge!(@path)
14
+ @path = nil
15
+ end
16
+ end
17
+
18
+ before_assert do
19
+ @options[:controller] ||= controller_name
20
+ @populated_path = @path.dup
21
+
22
+ @options.each do |key, value|
23
+ @options[key] = value.to_param if value.respond_to?(:to_param)
24
+ @populated_path.gsub!(key.inspect, value.to_s)
25
+ end
26
+
27
+ ::ActionController::Routing::Routes.reload if ::ActionController::Routing::Routes.empty?
28
+ end
29
+
30
+ def to(value)
31
+ @options.merge!(value)
32
+ self
33
+ end
34
+
35
+ def from(value)
36
+ @path = value
37
+ self
38
+ end
39
+
40
+ private
41
+
42
+ def map_to_path?
43
+ route_for = ::ActionController::Routing::Routes.generate(@options) rescue nil
44
+ return route_for == @populated_path, :actual => route_for.inspect
45
+ end
46
+
47
+ def generate_params?
48
+ env = ::ActionController::Routing::Routes.extract_request_environment(request) if request
49
+
50
+ env ||= {}
51
+ env[:method] = @method.to_sym
52
+ params_from = ::ActionController::Routing::Routes.recognize_path(@populated_path, env) rescue nil
53
+ return params_from == @options, :actual => params_from.inspect
54
+ end
55
+
56
+ def controller
57
+ @controller ||= if @subject.is_a?(::ActionController::Base)
58
+ @subject
59
+ elsif @spec.respond_to?(:controller)
60
+ @spec.controller
61
+ else
62
+ raise "Could not find a controller for route specs."
63
+ end
64
+ end
65
+
66
+ # First tries to get the controller name from the subject, then from
67
+ # the spec class using controller class or finally, from the described
68
+ # class.
69
+ #
70
+ # We have to try the described class because we don't have neither the
71
+ # subject or the controller class in the RoutingExampleGroup.
72
+ #
73
+ def controller_name
74
+ if controller_class
75
+ controller_class.name.gsub(/Controller$/, '').tableize
76
+ else
77
+ raise ArgumentError, "I cannot guess the controller name in route. Please supply :controller as option"
78
+ end
79
+ end
80
+
81
+ def controller_class
82
+ @controller_class ||= begin
83
+ spec_class = @spec.class unless @spec.class == Class
84
+
85
+ attempts = []
86
+ attempts << controller.class if controller
87
+ attempts << spec_class.controller_class if spec_class.respond_to?(:controller_class)
88
+ attempts << spec_class.described_class if spec_class.respond_to?(:described_class)
89
+
90
+ # Check for not blank names to address an odd rspec/rails behavior.
91
+ attempts.find { |klass| ::ActionController::Base >= klass && !klass.name.blank? }
92
+ end
93
+ end
94
+
95
+ def request
96
+ controller.request
97
+ end
98
+
99
+ def interpolation_options
100
+ { :options => @options.inspect, :method => @method.to_s.upcase, :path => @path.inspect }
101
+ end
102
+ end
103
+
104
+ # Assert route generation AND route recognition.
105
+ #
106
+ # == Examples
107
+ #
108
+ # # autodetects the :controller
109
+ # should_route :get, '/posts', :action => :index
110
+ #
111
+ # # explicitly specify :controller
112
+ # should_route :post, '/posts', :controller => :posts, :action => :create
113
+ #
114
+ # # non-string parameter
115
+ # should_route :get, '/posts/1', :controller => :posts, :action => :show, :id => 1
116
+ #
117
+ # # string-parameter
118
+ # should_route :put, '/posts/1', :controller => :posts, :action => :update, :id => "1"
119
+ # should_route :delete, '/posts/1', :controller => :posts, :action => :destroy, :id => 1
120
+ # should_route :get, '/posts/new', :controller => :posts, :action => :new
121
+ #
122
+ # # nested routes
123
+ # should_route :get, '/users/5/posts', :controller => :posts, :action => :index, :user_id => 5
124
+ # should_route :post, '/users/5/posts', :controller => :posts, :action => :create, :user_id => 5
125
+ #
126
+ # # it example
127
+ # it { should route(:get, :action => :index).to('/users/5/posts') }
128
+ #
129
+ def route(*params, &block)
130
+ RouteMatcher.new(*params, &block).spec(self)
131
+ end
132
+
133
+ end
134
+ end
135
+ end