remarkable_rails 3.0.0

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 (41) hide show
  1. data/CHANGELOG +81 -0
  2. data/LICENSE +20 -0
  3. data/README +2 -0
  4. data/lib/remarkable_rails/action_controller/base.rb +31 -0
  5. data/lib/remarkable_rails/action_controller/macro_stubs.rb +518 -0
  6. data/lib/remarkable_rails/action_controller/matchers/assign_to_matcher.rb +94 -0
  7. data/lib/remarkable_rails/action_controller/matchers/filter_params_matcher.rb +41 -0
  8. data/lib/remarkable_rails/action_controller/matchers/redirect_to_matcher.rb +119 -0
  9. data/lib/remarkable_rails/action_controller/matchers/render_template_matcher.rb +147 -0
  10. data/lib/remarkable_rails/action_controller/matchers/respond_with_matcher.rb +125 -0
  11. data/lib/remarkable_rails/action_controller/matchers/route_matcher.rb +94 -0
  12. data/lib/remarkable_rails/action_controller/matchers/set_session_matcher.rb +108 -0
  13. data/lib/remarkable_rails/action_controller/matchers/set_the_flash_matcher.rb +55 -0
  14. data/lib/remarkable_rails/action_controller.rb +22 -0
  15. data/lib/remarkable_rails/action_view/base.rb +7 -0
  16. data/lib/remarkable_rails/action_view.rb +18 -0
  17. data/lib/remarkable_rails/active_orm.rb +19 -0
  18. data/lib/remarkable_rails.rb +30 -0
  19. data/locale/en.yml +87 -0
  20. data/spec/action_controller/assign_to_matcher_spec.rb +143 -0
  21. data/spec/action_controller/filter_params_matcher_spec.rb +64 -0
  22. data/spec/action_controller/macro_stubs_spec.rb +196 -0
  23. data/spec/action_controller/redirect_to_matcher_spec.rb +102 -0
  24. data/spec/action_controller/render_template_matcher_spec.rb +251 -0
  25. data/spec/action_controller/respond_with_matcher_spec.rb +223 -0
  26. data/spec/action_controller/route_matcher_spec.rb +75 -0
  27. data/spec/action_controller/set_session_matcher_spec.rb +135 -0
  28. data/spec/action_controller/set_the_flash_matcher_spec.rb +95 -0
  29. data/spec/application/application.rb +15 -0
  30. data/spec/application/examples/_example.html.erb +0 -0
  31. data/spec/application/examples/example.html.erb +0 -0
  32. data/spec/application/examples/example.xml.builder +0 -0
  33. data/spec/application/examples/new.html.erb +0 -0
  34. data/spec/application/layouts/examples.html.erb +0 -0
  35. data/spec/application/projects/new.html.erb +0 -0
  36. data/spec/application/tasks_controller.rb +34 -0
  37. data/spec/functional_builder.rb +93 -0
  38. data/spec/rcov.opts +2 -0
  39. data/spec/spec.opts +4 -0
  40. data/spec/spec_helper.rb +44 -0
  41. metadata +134 -0
@@ -0,0 +1,94 @@
1
+ module Remarkable
2
+ module ActionController
3
+ module Matchers
4
+ class AssignToMatcher < Remarkable::ActionController::Base #:nodoc:
5
+ arguments :collection => :names, :as => :name, :block => :block
6
+
7
+ optional :with, :with_kind_of
8
+ collection_assertions :assigned_value?, :is_kind_of?, :is_equal_value?
9
+
10
+ before_assert :evaluate_expected_value
11
+
12
+ default_options :with_expectations => true
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
+ end
37
+
38
+ def value_to_compare?
39
+ @options.key?(:with) || @block
40
+ end
41
+
42
+ # Update interpolation options
43
+ def interpolation_options
44
+ if @subject && @subject.response
45
+ { :assign_inspect => assigns[@name].inspect, :assign_class => assigns[@name].class.name }
46
+ else
47
+ { }
48
+ end
49
+ end
50
+
51
+ # Evaluate procs before assert to avoid them appearing in descriptions.
52
+ def evaluate_expected_value
53
+ if value_to_compare?
54
+ value = @options.key?(:with) ? @options[:with] : @block
55
+ value = @spec.instance_eval(&value) if value.is_a?(Proc)
56
+ @options[:with] = value
57
+ end
58
+ end
59
+
60
+ end
61
+
62
+ # Checks if the controller assigned the variables given by name. If you
63
+ # want to check that a variable is not being assigned, please do:
64
+ #
65
+ # should_not_assign_to(:user)
66
+ #
67
+ # If you want to assure that a variable is being assigned to nil, do instead:
68
+ #
69
+ # should_assign_to(:user).with(nil)
70
+ #
71
+ # == Options
72
+ #
73
+ # * <tt>:with</tt> - The value to compare the assign.
74
+ # It can be also be supplied as proc or as a block (see examples below)
75
+ #
76
+ # * <tt>:with_kind_of</tt> - The expected class of the assign.
77
+ #
78
+ # == Examples
79
+ #
80
+ # should_assign_to :user, :with_kind_of => User
81
+ # should_assign_to :user, :with => proc{ users(:first) }
82
+ # should_assign_to(:user){ users(:first) }
83
+ #
84
+ # it { should assign_to(:user) }
85
+ # it { should assign_to(:user, :with => users(:first)) }
86
+ # it { should assign_to(:user, :with_kind_of => User) }
87
+ #
88
+ def assign_to(*args, &block)
89
+ AssignToMatcher.new(*args, &block).spec(self)
90
+ end
91
+
92
+ end
93
+ end
94
+ 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)
35
+ FilterParamsMatcher.new(*params).spec(self)
36
+ end
37
+ alias :filter_param :filter_params
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,119 @@
1
+ module Remarkable
2
+ module ActionController
3
+ module Matchers
4
+ class RedirectToMatcher < Remarkable::ActionController::Base #:nodoc:
5
+ include ::ActionController::StatusCodes
6
+
7
+ arguments :expected, :block => :block
8
+ optional :with
9
+
10
+ assertions :redirected?, :status_matches?, :url_matches?
11
+
12
+ before_assert :evaluate_expected_value
13
+
14
+ before_assert do
15
+ @response = @subject.respond_to?(:response) ? @subject.response : @subject
16
+ @request = @response.instance_variable_get('@request')
17
+ end
18
+
19
+ protected
20
+
21
+ def redirected?
22
+ @response.redirect?
23
+ end
24
+
25
+ def status_matches?
26
+ return true unless @options.key?(:with)
27
+
28
+ actual_status = interpret_status(@response.response_code)
29
+ expected_status = interpret_status(@options[:with])
30
+
31
+ return actual_status == expected_status, :status => @response.response_code.inspect
32
+ end
33
+
34
+ def url_matches?
35
+ @actual = @response.redirect_url
36
+
37
+ if @expected.instance_of?(Hash)
38
+ return false unless @actual =~ /^\w+:\/\/#{@request.host}/ && actual_hash
39
+ actual_hash == expected_hash
40
+ else
41
+ @actual == expected_url
42
+ end
43
+ end
44
+
45
+ def actual_hash
46
+ hash_from_url @actual
47
+ end
48
+
49
+ def expected_hash
50
+ hash_from_url expected_url
51
+ end
52
+
53
+ def hash_from_url(url)
54
+ query_hash(url).merge(path_hash(url)).with_indifferent_access
55
+ end
56
+
57
+ def path_hash(url)
58
+ path = url.sub(/^\w+:\/\/#{@request.host}(?::\d+)?/, "").split("?", 2)[0]
59
+ ::ActionController::Routing::Routes.recognize_path path, { :method => :get }
60
+ end
61
+
62
+ def query_hash(url)
63
+ query = url.split("?", 2)[1] || ""
64
+
65
+ if defined?(::Rack::Utils)
66
+ ::Rack::Utils.parse_query(query)
67
+ else
68
+ @request.class.parse_query_parameters(query)
69
+ end
70
+ end
71
+
72
+ def expected_url
73
+ case @expected
74
+ when Hash
75
+ return ::ActionController::UrlRewriter.new(@request, {}).rewrite(@expected)
76
+ when :back
77
+ return @request.env['HTTP_REFERER']
78
+ when %r{^\w+://.*}
79
+ return @expected
80
+ else
81
+ return "http://#{@request.host}" + (@expected.split('')[0] == '/' ? '' : '/') + @expected
82
+ end
83
+ end
84
+
85
+ def interpolation_options
86
+ { :expected => @expected.inspect, :actual => @actual.inspect }
87
+ end
88
+
89
+ def evaluate_expected_value
90
+ @expected ||= @block if @block
91
+ @expected = @spec.instance_eval(&@expected) if @expected.is_a?(Proc)
92
+ end
93
+
94
+ end
95
+
96
+ # Passes if the response redirects to the given url. The url can be a string,
97
+ # a hash or can be supplied as a block (see examples below).
98
+ #
99
+ # == Options
100
+ #
101
+ # * <tt>:with</tt> - The status 30X used when redirecting.
102
+ #
103
+ # == Examples
104
+ #
105
+ # should_redirect_to{ users_url }
106
+ # should_redirect_to(:action => 'index')
107
+ # should_not_redirect_to(:controller => 'users', :action => 'new')
108
+ #
109
+ # it { should redirect_to(users_url).with(302) }
110
+ # it { should redirect_to(:action => 'index') }
111
+ # it { should_not redirect_to(:controller => 'users', :action => 'new') }
112
+ #
113
+ def redirect_to(expected=nil, options={}, &block)
114
+ RedirectToMatcher.new(expected, options, &block).spec(self)
115
+ end
116
+
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,147 @@
1
+ require File.join(File.dirname(__FILE__), 'respond_with_matcher')
2
+
3
+ module Remarkable
4
+ module ActionController
5
+ module Matchers
6
+ class RenderTemplateMatcher < RespondWithMatcher #:nodoc:
7
+
8
+ prepend_optional :template, :layout
9
+
10
+ assertions :rendered?, :template_matches?, :layout_matches?
11
+
12
+ protected
13
+
14
+ def rendered?
15
+ return true unless @options.key?(:template)
16
+
17
+ @actual = if @response.respond_to?(:rendered_file)
18
+ @response.rendered_file
19
+ elsif @response.respond_to?(:rendered)
20
+ case template = @response.rendered[:template]
21
+ when nil
22
+ unless @response.rendered[:partials].empty?
23
+ path_and_file(response.rendered[:partials].keys.first).join("/_")
24
+ end
25
+ when ::ActionView::Template
26
+ template.path
27
+ when ::String
28
+ template
29
+ end
30
+ else
31
+ @response.rendered_template.to_s
32
+ end
33
+
34
+ !@actual.blank?
35
+ end
36
+
37
+ def template_matches?
38
+ return true unless @options[:template] # only continue if not nil
39
+
40
+ actual_controller_path, actual_file = path_and_file(@actual.to_s)
41
+ expected_controller_path, expected_file = path_and_file(@options[:template].to_s)
42
+
43
+ # Test if each given slice matches. Actual always return the full
44
+ # file name (new.html.erb), on the other hand, the user might supply
45
+ # only new. If the user supply all three pieces, we check if they
46
+ # are equal, in the given order.
47
+ #
48
+ actual_file = actual_file.split('.')
49
+ expected_file.split('.').each_with_index do |slice, i|
50
+ return false unless slice == actual_file[i] || actual_file[i].nil?
51
+ end
52
+
53
+ actual_controller_path == expected_controller_path
54
+ end
55
+
56
+ def layout_matches?
57
+ return true unless @options.key?(:layout)
58
+ @response.layout.to_s.split('/').last.to_s == @options[:layout].to_s
59
+ end
60
+
61
+ def path_and_file(path)
62
+ parts = path.split('/')
63
+ file = parts.pop
64
+ controller = parts.empty? ? @controller.controller_path : parts.join('/')
65
+ return controller, file
66
+ end
67
+
68
+ def interpolation_options
69
+ if @response
70
+ super.merge!(:actual_layout => @response.layout.inspect, :actual_template => @actual.inspect)
71
+ else
72
+ super.merge!(:actual_template => @actual.inspect)
73
+ end
74
+ end
75
+
76
+ end
77
+
78
+ # Passes if the specified template (view file) is rendered by the
79
+ # response. This file can be any view file, including a partial.
80
+ #
81
+ # <code>template</code> can include the controller path. It can also
82
+ # include an optional extension, which you only need to use when there
83
+ # is ambiguity.
84
+ #
85
+ # Note that partials must be spelled with the preceding underscore.
86
+ #
87
+ # == Options
88
+ #
89
+ # * <tt>:layout</tt> - The layout used when rendering the template.
90
+ #
91
+ # All other options in <tt>respond_with</tt> are also available.
92
+ #
93
+ # == Examples
94
+ #
95
+ # should_render_template 'list'
96
+ # should_render_template 'same_controller/list'
97
+ # should_render_template 'other_controller/list'
98
+ #
99
+ # # with extensions
100
+ # should_render_template 'list.rjs'
101
+ # should_render_template 'list.haml'
102
+ # should_render_template 'same_controller/list.rjs'
103
+ # should_render_template 'other_controller/list.rjs'
104
+ #
105
+ # # partials
106
+ # should_render_template '_a_partial'
107
+ # should_render_template 'same_controller/_a_partial'
108
+ # should_render_template 'other_controller/_a_partial'
109
+ #
110
+ # # with options
111
+ # should_render_template 'list', :layout => 'users'
112
+ # should_render_template 'list', :content_type => :xml
113
+ # should_render_template 'list', :content_type => /xml/
114
+ # should_render_template 'list', :content_type => Mime::XML
115
+ #
116
+ # it { should render_template('list').layout('users') }
117
+ # it { should render_template('list').content_type(:xml) }
118
+ # it { should render_template('list').content_type(/xml/) }
119
+ # it { should render_template('list').content_type(Mime::XML) }
120
+ #
121
+ # == Gotcha
122
+ #
123
+ # Extensions check does not work in Rails 2.1.x.
124
+ #
125
+ def render_template(*args)
126
+ options = args.extract_options!
127
+ RenderTemplateMatcher.new(options.merge(:template => args.first)).spec(self)
128
+ end
129
+
130
+ # This is just a shortcut for render_template :layout => layout. It's also
131
+ # used for Shoulda compatibility. Check render_template for more information.
132
+ #
133
+ def render_with_layout(*args)
134
+ options = args.extract_options!
135
+ RenderTemplateMatcher.new(options.merge(:layout => args.first)).spec(self)
136
+ end
137
+
138
+ # This is just a shortcut for render_template :layout => nil. It's also
139
+ # used for Shoulda compatibility. Check render_template for more information.
140
+ #
141
+ def render_without_layout(options={})
142
+ RenderTemplateMatcher.new(options.merge(:layout => nil)).spec(self)
143
+ end
144
+
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,125 @@
1
+ module Remarkable
2
+ module ActionController
3
+ module Matchers
4
+ class RespondWithMatcher < Remarkable::ActionController::Base #:nodoc:
5
+
6
+ arguments
7
+
8
+ optional :with, :body, :content_type
9
+
10
+ before_assert do
11
+ @response = @subject.respond_to?(:response) ? @subject.response : @subject
12
+ @controller = @spec.instance_variable_get('@controller')
13
+ end
14
+
15
+ before_assert :evaluate_content_type
16
+
17
+ assertions :status_matches?, :body_matches?, :content_type_matches?
18
+
19
+ protected
20
+
21
+ def status_matches?
22
+ return true unless @options[:with] # only continue if not nil
23
+
24
+ case @options[:with]
25
+ when :success, :missing, :redirect, :error
26
+ @response.send("#{@options[:with]}?")
27
+ when Fixnum
28
+ @response.response_code == @options[:with]
29
+ when Symbol, String
30
+ @response.response_code == ::ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE[@options[:with].to_sym]
31
+ when Range
32
+ @options[:with].include?(@response.response_code)
33
+ else
34
+ raise ArgumentError, "I don't know how to interpret status #{@options[:with].inspect}, " <<
35
+ "please give me a Fixnum, Symbol, String or Range."
36
+ end
37
+ end
38
+
39
+ def body_matches?
40
+ return true unless @options.key?(:body)
41
+ assert_contains(@response.body, @options[:body])
42
+ end
43
+
44
+ def content_type_matches?
45
+ return true unless @options.key?(:content_type)
46
+ assert_contains(@response.content_type, @options[:content_type])
47
+ end
48
+
49
+ # Evaluate content_type before assertions to have nice descriptions
50
+ def evaluate_content_type
51
+ return unless @options.key?(:content_type)
52
+
53
+ @options[:content_type] = case @options[:content_type]
54
+ when Symbol
55
+ Mime::Type.lookup_by_extension(@options[:content_type].to_s).to_s
56
+ when Regexp
57
+ @options[:content_type]
58
+ else
59
+ @options[:content_type].to_s
60
+ end
61
+ end
62
+
63
+ def interpolation_options
64
+ if @response
65
+ { :actual_body => @response.body.inspect,
66
+ :actual_status => @response.response_code.inspect,
67
+ :actual_content_type => @response.content_type.inspect }
68
+ else
69
+ { }
70
+ end
71
+ end
72
+
73
+ end
74
+
75
+ # Passes if the response has the given status. Status can be a Symbol lik
76
+ # :success, :missing, :redirect and :error. Can be also a Fixnum, Range o
77
+ # any other symbol which matches to any of Rails status codes.
78
+ #
79
+ # == Options
80
+ #
81
+ # * <tt>:body</tt> - The body of the response. It accepts strings and or
82
+ # regular expressions. Altought you might be running your tests without
83
+ # integrating your views, this is useful when rendering :xml or :text.
84
+ #
85
+ # * <tt>:content_type</tt> - The content type of the response.
86
+ # It accepts strings ('application/rss+xml'), mime constants (Mime::RSS),
87
+ # symbols (:rss) and regular expressions /rss/.
88
+ #
89
+ # == Examples
90
+ #
91
+ # should_respond_with :success
92
+ # should_respond_with :error, :body => /System error/
93
+ # should_respond_with 301, :content_type => Mime::XML
94
+ # should_respond_with 300..399, :content_type => Mime::XML
95
+ #
96
+ # it { should respond_with(:success) }
97
+ # it { should respond_with(:error).body(/System error/) }
98
+ # it { should respond_with(301).content_type(Mime::XML) }
99
+ # it { should respond_with(300..399).content_type(Mime::XML) }
100
+ #
101
+ def respond_with(*args)
102
+ options = args.extract_options!
103
+ RespondWithMatcher.new(options.merge(:with => args.first)).spec(self)
104
+ end
105
+
106
+ # This is just a shortcut for respond_with :body => body. Check respond_with
107
+ # for more information.
108
+ #
109
+ def respond_with_body(*args)
110
+ options = args.extract_options!
111
+ RespondWithMatcher.new(options.merge(:body => args.first)).spec(self)
112
+ end
113
+
114
+ # This is just a shortcut for respond_with :content_type => content_type.
115
+ # It's also used for Shoulda compatibility. Check respond_with for more
116
+ # information.
117
+ #
118
+ def respond_with_content_type(*args)
119
+ options = args.extract_options!
120
+ RespondWithMatcher.new(options.merge(:content_type => args.first)).spec(self)
121
+ end
122
+
123
+ end
124
+ end
125
+ end