benhutton-remarkable_rails 4.0.0.alpha4

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.
@@ -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