halorgium-actionpack 3.0.pre

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 (154) hide show
  1. data/CHANGELOG +5179 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +409 -0
  4. data/lib/abstract_controller.rb +16 -0
  5. data/lib/abstract_controller/base.rb +158 -0
  6. data/lib/abstract_controller/callbacks.rb +113 -0
  7. data/lib/abstract_controller/exceptions.rb +12 -0
  8. data/lib/abstract_controller/helpers.rb +151 -0
  9. data/lib/abstract_controller/layouts.rb +250 -0
  10. data/lib/abstract_controller/localized_cache.rb +49 -0
  11. data/lib/abstract_controller/logger.rb +61 -0
  12. data/lib/abstract_controller/rendering_controller.rb +188 -0
  13. data/lib/action_controller.rb +72 -0
  14. data/lib/action_controller/base.rb +168 -0
  15. data/lib/action_controller/caching.rb +80 -0
  16. data/lib/action_controller/caching/actions.rb +163 -0
  17. data/lib/action_controller/caching/fragments.rb +116 -0
  18. data/lib/action_controller/caching/pages.rb +154 -0
  19. data/lib/action_controller/caching/sweeping.rb +97 -0
  20. data/lib/action_controller/deprecated.rb +4 -0
  21. data/lib/action_controller/deprecated/integration_test.rb +2 -0
  22. data/lib/action_controller/deprecated/performance_test.rb +1 -0
  23. data/lib/action_controller/dispatch/dispatcher.rb +57 -0
  24. data/lib/action_controller/metal.rb +129 -0
  25. data/lib/action_controller/metal/benchmarking.rb +73 -0
  26. data/lib/action_controller/metal/compatibility.rb +145 -0
  27. data/lib/action_controller/metal/conditional_get.rb +86 -0
  28. data/lib/action_controller/metal/configuration.rb +28 -0
  29. data/lib/action_controller/metal/cookies.rb +105 -0
  30. data/lib/action_controller/metal/exceptions.rb +55 -0
  31. data/lib/action_controller/metal/filter_parameter_logging.rb +77 -0
  32. data/lib/action_controller/metal/flash.rb +162 -0
  33. data/lib/action_controller/metal/head.rb +27 -0
  34. data/lib/action_controller/metal/helpers.rb +115 -0
  35. data/lib/action_controller/metal/hide_actions.rb +47 -0
  36. data/lib/action_controller/metal/http_authentication.rb +312 -0
  37. data/lib/action_controller/metal/layouts.rb +171 -0
  38. data/lib/action_controller/metal/mime_responds.rb +317 -0
  39. data/lib/action_controller/metal/rack_convenience.rb +27 -0
  40. data/lib/action_controller/metal/redirector.rb +22 -0
  41. data/lib/action_controller/metal/render_options.rb +103 -0
  42. data/lib/action_controller/metal/rendering_controller.rb +57 -0
  43. data/lib/action_controller/metal/request_forgery_protection.rb +108 -0
  44. data/lib/action_controller/metal/rescuable.rb +13 -0
  45. data/lib/action_controller/metal/responder.rb +200 -0
  46. data/lib/action_controller/metal/session.rb +15 -0
  47. data/lib/action_controller/metal/session_management.rb +45 -0
  48. data/lib/action_controller/metal/streaming.rb +188 -0
  49. data/lib/action_controller/metal/testing.rb +39 -0
  50. data/lib/action_controller/metal/url_for.rb +41 -0
  51. data/lib/action_controller/metal/verification.rb +130 -0
  52. data/lib/action_controller/middleware.rb +38 -0
  53. data/lib/action_controller/notifications.rb +10 -0
  54. data/lib/action_controller/polymorphic_routes.rb +183 -0
  55. data/lib/action_controller/record_identifier.rb +91 -0
  56. data/lib/action_controller/testing/process.rb +111 -0
  57. data/lib/action_controller/testing/test_case.rb +345 -0
  58. data/lib/action_controller/translation.rb +13 -0
  59. data/lib/action_controller/url_rewriter.rb +204 -0
  60. data/lib/action_controller/vendor/html-scanner.rb +16 -0
  61. data/lib/action_controller/vendor/html-scanner/html/document.rb +68 -0
  62. data/lib/action_controller/vendor/html-scanner/html/node.rb +537 -0
  63. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +176 -0
  64. data/lib/action_controller/vendor/html-scanner/html/selector.rb +828 -0
  65. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +105 -0
  66. data/lib/action_controller/vendor/html-scanner/html/version.rb +11 -0
  67. data/lib/action_dispatch.rb +70 -0
  68. data/lib/action_dispatch/http/headers.rb +33 -0
  69. data/lib/action_dispatch/http/mime_type.rb +231 -0
  70. data/lib/action_dispatch/http/mime_types.rb +23 -0
  71. data/lib/action_dispatch/http/request.rb +539 -0
  72. data/lib/action_dispatch/http/response.rb +290 -0
  73. data/lib/action_dispatch/http/status_codes.rb +42 -0
  74. data/lib/action_dispatch/http/utils.rb +20 -0
  75. data/lib/action_dispatch/middleware/callbacks.rb +50 -0
  76. data/lib/action_dispatch/middleware/params_parser.rb +79 -0
  77. data/lib/action_dispatch/middleware/rescue.rb +26 -0
  78. data/lib/action_dispatch/middleware/session/abstract_store.rb +208 -0
  79. data/lib/action_dispatch/middleware/session/cookie_store.rb +235 -0
  80. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +47 -0
  81. data/lib/action_dispatch/middleware/show_exceptions.rb +143 -0
  82. data/lib/action_dispatch/middleware/stack.rb +116 -0
  83. data/lib/action_dispatch/middleware/static.rb +44 -0
  84. data/lib/action_dispatch/middleware/string_coercion.rb +29 -0
  85. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +24 -0
  86. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +26 -0
  87. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +10 -0
  88. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +29 -0
  89. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +2 -0
  90. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +10 -0
  91. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +21 -0
  92. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +2 -0
  93. data/lib/action_dispatch/routing.rb +381 -0
  94. data/lib/action_dispatch/routing/deprecated_mapper.rb +878 -0
  95. data/lib/action_dispatch/routing/mapper.rb +327 -0
  96. data/lib/action_dispatch/routing/route.rb +49 -0
  97. data/lib/action_dispatch/routing/route_set.rb +497 -0
  98. data/lib/action_dispatch/testing/assertions.rb +8 -0
  99. data/lib/action_dispatch/testing/assertions/dom.rb +35 -0
  100. data/lib/action_dispatch/testing/assertions/model.rb +19 -0
  101. data/lib/action_dispatch/testing/assertions/response.rb +145 -0
  102. data/lib/action_dispatch/testing/assertions/routing.rb +144 -0
  103. data/lib/action_dispatch/testing/assertions/selector.rb +639 -0
  104. data/lib/action_dispatch/testing/assertions/tag.rb +123 -0
  105. data/lib/action_dispatch/testing/integration.rb +504 -0
  106. data/lib/action_dispatch/testing/performance_test.rb +15 -0
  107. data/lib/action_dispatch/testing/test_request.rb +83 -0
  108. data/lib/action_dispatch/testing/test_response.rb +131 -0
  109. data/lib/action_pack.rb +24 -0
  110. data/lib/action_pack/version.rb +9 -0
  111. data/lib/action_view.rb +58 -0
  112. data/lib/action_view/base.rb +308 -0
  113. data/lib/action_view/context.rb +44 -0
  114. data/lib/action_view/erb/util.rb +48 -0
  115. data/lib/action_view/helpers.rb +62 -0
  116. data/lib/action_view/helpers/active_model_helper.rb +306 -0
  117. data/lib/action_view/helpers/ajax_helper.rb +68 -0
  118. data/lib/action_view/helpers/asset_tag_helper.rb +830 -0
  119. data/lib/action_view/helpers/atom_feed_helper.rb +198 -0
  120. data/lib/action_view/helpers/cache_helper.rb +39 -0
  121. data/lib/action_view/helpers/capture_helper.rb +168 -0
  122. data/lib/action_view/helpers/date_helper.rb +988 -0
  123. data/lib/action_view/helpers/debug_helper.rb +38 -0
  124. data/lib/action_view/helpers/form_helper.rb +1102 -0
  125. data/lib/action_view/helpers/form_options_helper.rb +600 -0
  126. data/lib/action_view/helpers/form_tag_helper.rb +495 -0
  127. data/lib/action_view/helpers/javascript_helper.rb +208 -0
  128. data/lib/action_view/helpers/number_helper.rb +311 -0
  129. data/lib/action_view/helpers/prototype_helper.rb +1309 -0
  130. data/lib/action_view/helpers/raw_output_helper.rb +9 -0
  131. data/lib/action_view/helpers/record_identification_helper.rb +20 -0
  132. data/lib/action_view/helpers/record_tag_helper.rb +58 -0
  133. data/lib/action_view/helpers/sanitize_helper.rb +259 -0
  134. data/lib/action_view/helpers/scriptaculous_helper.rb +226 -0
  135. data/lib/action_view/helpers/tag_helper.rb +151 -0
  136. data/lib/action_view/helpers/text_helper.rb +594 -0
  137. data/lib/action_view/helpers/translation_helper.rb +39 -0
  138. data/lib/action_view/helpers/url_helper.rb +639 -0
  139. data/lib/action_view/locale/en.yml +117 -0
  140. data/lib/action_view/paths.rb +80 -0
  141. data/lib/action_view/render/partials.rb +342 -0
  142. data/lib/action_view/render/rendering.rb +134 -0
  143. data/lib/action_view/safe_buffer.rb +28 -0
  144. data/lib/action_view/template/error.rb +101 -0
  145. data/lib/action_view/template/handler.rb +36 -0
  146. data/lib/action_view/template/handlers.rb +52 -0
  147. data/lib/action_view/template/handlers/builder.rb +17 -0
  148. data/lib/action_view/template/handlers/erb.rb +53 -0
  149. data/lib/action_view/template/handlers/rjs.rb +18 -0
  150. data/lib/action_view/template/resolver.rb +165 -0
  151. data/lib/action_view/template/template.rb +131 -0
  152. data/lib/action_view/template/text.rb +38 -0
  153. data/lib/action_view/test_case.rb +163 -0
  154. metadata +236 -0
@@ -0,0 +1,123 @@
1
+ module ActionDispatch
2
+ module Assertions
3
+ # Pair of assertions to testing elements in the HTML output of the response.
4
+ module TagAssertions
5
+ # Asserts that there is a tag/node/element in the body of the response
6
+ # that meets all of the given conditions. The +conditions+ parameter must
7
+ # be a hash of any of the following keys (all are optional):
8
+ #
9
+ # * <tt>:tag</tt>: the node type must match the corresponding value
10
+ # * <tt>:attributes</tt>: a hash. The node's attributes must match the
11
+ # corresponding values in the hash.
12
+ # * <tt>:parent</tt>: a hash. The node's parent must match the
13
+ # corresponding hash.
14
+ # * <tt>:child</tt>: a hash. At least one of the node's immediate children
15
+ # must meet the criteria described by the hash.
16
+ # * <tt>:ancestor</tt>: a hash. At least one of the node's ancestors must
17
+ # meet the criteria described by the hash.
18
+ # * <tt>:descendant</tt>: a hash. At least one of the node's descendants
19
+ # must meet the criteria described by the hash.
20
+ # * <tt>:sibling</tt>: a hash. At least one of the node's siblings must
21
+ # meet the criteria described by the hash.
22
+ # * <tt>:after</tt>: a hash. The node must be after any sibling meeting
23
+ # the criteria described by the hash, and at least one sibling must match.
24
+ # * <tt>:before</tt>: a hash. The node must be before any sibling meeting
25
+ # the criteria described by the hash, and at least one sibling must match.
26
+ # * <tt>:children</tt>: a hash, for counting children of a node. Accepts
27
+ # the keys:
28
+ # * <tt>:count</tt>: either a number or a range which must equal (or
29
+ # include) the number of children that match.
30
+ # * <tt>:less_than</tt>: the number of matching children must be less
31
+ # than this number.
32
+ # * <tt>:greater_than</tt>: the number of matching children must be
33
+ # greater than this number.
34
+ # * <tt>:only</tt>: another hash consisting of the keys to use
35
+ # to match on the children, and only matching children will be
36
+ # counted.
37
+ # * <tt>:content</tt>: the textual content of the node must match the
38
+ # given value. This will not match HTML tags in the body of a
39
+ # tag--only text.
40
+ #
41
+ # Conditions are matched using the following algorithm:
42
+ #
43
+ # * if the condition is a string, it must be a substring of the value.
44
+ # * if the condition is a regexp, it must match the value.
45
+ # * if the condition is a number, the value must match number.to_s.
46
+ # * if the condition is +true+, the value must not be +nil+.
47
+ # * if the condition is +false+ or +nil+, the value must be +nil+.
48
+ #
49
+ # === Examples
50
+ #
51
+ # # Assert that there is a "span" tag
52
+ # assert_tag :tag => "span"
53
+ #
54
+ # # Assert that there is a "span" tag with id="x"
55
+ # assert_tag :tag => "span", :attributes => { :id => "x" }
56
+ #
57
+ # # Assert that there is a "span" tag using the short-hand
58
+ # assert_tag :span
59
+ #
60
+ # # Assert that there is a "span" tag with id="x" using the short-hand
61
+ # assert_tag :span, :attributes => { :id => "x" }
62
+ #
63
+ # # Assert that there is a "span" inside of a "div"
64
+ # assert_tag :tag => "span", :parent => { :tag => "div" }
65
+ #
66
+ # # Assert that there is a "span" somewhere inside a table
67
+ # assert_tag :tag => "span", :ancestor => { :tag => "table" }
68
+ #
69
+ # # Assert that there is a "span" with at least one "em" child
70
+ # assert_tag :tag => "span", :child => { :tag => "em" }
71
+ #
72
+ # # Assert that there is a "span" containing a (possibly nested)
73
+ # # "strong" tag.
74
+ # assert_tag :tag => "span", :descendant => { :tag => "strong" }
75
+ #
76
+ # # Assert that there is a "span" containing between 2 and 4 "em" tags
77
+ # # as immediate children
78
+ # assert_tag :tag => "span",
79
+ # :children => { :count => 2..4, :only => { :tag => "em" } }
80
+ #
81
+ # # Get funky: assert that there is a "div", with an "ul" ancestor
82
+ # # and an "li" parent (with "class" = "enum"), and containing a
83
+ # # "span" descendant that contains text matching /hello world/
84
+ # assert_tag :tag => "div",
85
+ # :ancestor => { :tag => "ul" },
86
+ # :parent => { :tag => "li",
87
+ # :attributes => { :class => "enum" } },
88
+ # :descendant => { :tag => "span",
89
+ # :child => /hello world/ }
90
+ #
91
+ # <b>Please note</b>: +assert_tag+ and +assert_no_tag+ only work
92
+ # with well-formed XHTML. They recognize a few tags as implicitly self-closing
93
+ # (like br and hr and such) but will not work correctly with tags
94
+ # that allow optional closing tags (p, li, td). <em>You must explicitly
95
+ # close all of your tags to use these assertions.</em>
96
+ def assert_tag(*opts)
97
+ opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
98
+ tag = find_tag(opts)
99
+ assert tag, "expected tag, but no tag found matching #{opts.inspect} in:\n#{@response.body.inspect}"
100
+ end
101
+
102
+ # Identical to +assert_tag+, but asserts that a matching tag does _not_
103
+ # exist. (See +assert_tag+ for a full discussion of the syntax.)
104
+ #
105
+ # === Examples
106
+ # # Assert that there is not a "div" containing a "p"
107
+ # assert_no_tag :tag => "div", :descendant => { :tag => "p" }
108
+ #
109
+ # # Assert that an unordered list is empty
110
+ # assert_no_tag :tag => "ul", :descendant => { :tag => "li" }
111
+ #
112
+ # # Assert that there is not a "p" tag with between 1 to 3 "img" tags
113
+ # # as immediate children
114
+ # assert_no_tag :tag => "p",
115
+ # :children => { :count => 1..3, :only => { :tag => "img" } }
116
+ def assert_no_tag(*opts)
117
+ opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
118
+ tag = find_tag(opts)
119
+ assert !tag, "expected no tag, but found tag matching #{opts.inspect} in:\n#{@response.body.inspect}"
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,504 @@
1
+ require 'stringio'
2
+ require 'uri'
3
+ require 'active_support/test_case'
4
+ require 'active_support/core_ext/object/metaclass'
5
+
6
+ # TODO: Remove circular dependency on ActionController
7
+ require 'action_controller/testing/process'
8
+
9
+ module ActionDispatch
10
+ module Integration #:nodoc:
11
+ module RequestHelpers
12
+ # Performs a GET request with the given parameters.
13
+ #
14
+ # - +path+: The URI (as a String) on which you want to perform a GET
15
+ # request.
16
+ # - +parameters+: The HTTP parameters that you want to pass. This may
17
+ # be +nil+,
18
+ # a Hash, or a String that is appropriately encoded
19
+ # (<tt>application/x-www-form-urlencoded</tt> or
20
+ # <tt>multipart/form-data</tt>).
21
+ # - +headers+: Additional HTTP headers to pass, as a Hash. The keys will
22
+ # automatically be upcased, with the prefix 'HTTP_' added if needed.
23
+ #
24
+ # This method returns an Response object, which one can use to
25
+ # inspect the details of the response. Furthermore, if this method was
26
+ # called from an ActionDispatch::IntegrationTest object, then that
27
+ # object's <tt>@response</tt> instance variable will point to the same
28
+ # response object.
29
+ #
30
+ # You can also perform POST, PUT, DELETE, and HEAD requests with +post+,
31
+ # +put+, +delete+, and +head+.
32
+ def get(path, parameters = nil, headers = nil)
33
+ process :get, path, parameters, headers
34
+ end
35
+
36
+ # Performs a POST request with the given parameters. See get() for more
37
+ # details.
38
+ def post(path, parameters = nil, headers = nil)
39
+ process :post, path, parameters, headers
40
+ end
41
+
42
+ # Performs a PUT request with the given parameters. See get() for more
43
+ # details.
44
+ def put(path, parameters = nil, headers = nil)
45
+ process :put, path, parameters, headers
46
+ end
47
+
48
+ # Performs a DELETE request with the given parameters. See get() for
49
+ # more details.
50
+ def delete(path, parameters = nil, headers = nil)
51
+ process :delete, path, parameters, headers
52
+ end
53
+
54
+ # Performs a HEAD request with the given parameters. See get() for more
55
+ # details.
56
+ def head(path, parameters = nil, headers = nil)
57
+ process :head, path, parameters, headers
58
+ end
59
+
60
+ # Performs an XMLHttpRequest request with the given parameters, mirroring
61
+ # a request from the Prototype library.
62
+ #
63
+ # The request_method is :get, :post, :put, :delete or :head; the
64
+ # parameters are +nil+, a hash, or a url-encoded or multipart string;
65
+ # the headers are a hash. Keys are automatically upcased and prefixed
66
+ # with 'HTTP_' if not already.
67
+ def xml_http_request(request_method, path, parameters = nil, headers = nil)
68
+ headers ||= {}
69
+ headers['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
70
+ headers['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
71
+ process(request_method, path, parameters, headers)
72
+ end
73
+ alias xhr :xml_http_request
74
+
75
+ # Follow a single redirect response. If the last response was not a
76
+ # redirect, an exception will be raised. Otherwise, the redirect is
77
+ # performed on the location header.
78
+ def follow_redirect!
79
+ raise "not a redirect! #{status} #{status_message}" unless redirect?
80
+ get(response.location)
81
+ status
82
+ end
83
+
84
+ # Performs a request using the specified method, following any subsequent
85
+ # redirect. Note that the redirects are followed until the response is
86
+ # not a redirect--this means you may run into an infinite loop if your
87
+ # redirect loops back to itself.
88
+ def request_via_redirect(http_method, path, parameters = nil, headers = nil)
89
+ process(http_method, path, parameters, headers)
90
+ follow_redirect! while redirect?
91
+ status
92
+ end
93
+
94
+ # Performs a GET request, following any subsequent redirect.
95
+ # See +request_via_redirect+ for more information.
96
+ def get_via_redirect(path, parameters = nil, headers = nil)
97
+ request_via_redirect(:get, path, parameters, headers)
98
+ end
99
+
100
+ # Performs a POST request, following any subsequent redirect.
101
+ # See +request_via_redirect+ for more information.
102
+ def post_via_redirect(path, parameters = nil, headers = nil)
103
+ request_via_redirect(:post, path, parameters, headers)
104
+ end
105
+
106
+ # Performs a PUT request, following any subsequent redirect.
107
+ # See +request_via_redirect+ for more information.
108
+ def put_via_redirect(path, parameters = nil, headers = nil)
109
+ request_via_redirect(:put, path, parameters, headers)
110
+ end
111
+
112
+ # Performs a DELETE request, following any subsequent redirect.
113
+ # See +request_via_redirect+ for more information.
114
+ def delete_via_redirect(path, parameters = nil, headers = nil)
115
+ request_via_redirect(:delete, path, parameters, headers)
116
+ end
117
+ end
118
+
119
+ # An integration Session instance represents a set of requests and responses
120
+ # performed sequentially by some virtual user. Because you can instantiate
121
+ # multiple sessions and run them side-by-side, you can also mimic (to some
122
+ # limited extent) multiple simultaneous users interacting with your system.
123
+ #
124
+ # Typically, you will instantiate a new session using
125
+ # IntegrationTest#open_session, rather than instantiating
126
+ # Integration::Session directly.
127
+ class Session
128
+ DEFAULT_HOST = "www.example.com"
129
+
130
+ include Test::Unit::Assertions
131
+ include ActionDispatch::Assertions
132
+ include ActionController::TestProcess
133
+ include RequestHelpers
134
+
135
+ %w( status status_message headers body redirect? ).each do |method|
136
+ delegate method, :to => :response, :allow_nil => true
137
+ end
138
+
139
+ %w( path ).each do |method|
140
+ delegate method, :to => :request, :allow_nil => true
141
+ end
142
+
143
+ # The hostname used in the last request.
144
+ attr_accessor :host
145
+
146
+ # The remote_addr used in the last request.
147
+ attr_accessor :remote_addr
148
+
149
+ # The Accept header to send.
150
+ attr_accessor :accept
151
+
152
+ # A map of the cookies returned by the last response, and which will be
153
+ # sent with the next request.
154
+ def cookies
155
+ @mock_session.cookie_jar
156
+ end
157
+
158
+ # A reference to the controller instance used by the last request.
159
+ attr_reader :controller
160
+
161
+ # A reference to the request instance used by the last request.
162
+ attr_reader :request
163
+
164
+ # A reference to the response instance used by the last request.
165
+ attr_reader :response
166
+
167
+ # A running counter of the number of requests processed.
168
+ attr_accessor :request_count
169
+
170
+ # Create and initialize a new Session instance.
171
+ def initialize(app)
172
+ @app = app
173
+ reset!
174
+ end
175
+
176
+ # Resets the instance. This can be used to reset the state information
177
+ # in an existing session instance, so it can be used from a clean-slate
178
+ # condition.
179
+ #
180
+ # session.reset!
181
+ def reset!
182
+ @https = false
183
+ @mock_session = Rack::MockSession.new(@app, DEFAULT_HOST)
184
+ @controller = @request = @response = nil
185
+ @request_count = 0
186
+
187
+ self.host = DEFAULT_HOST
188
+ self.remote_addr = "127.0.0.1"
189
+ self.accept = "text/xml,application/xml,application/xhtml+xml," +
190
+ "text/html;q=0.9,text/plain;q=0.8,image/png," +
191
+ "*/*;q=0.5"
192
+
193
+ unless defined? @named_routes_configured
194
+ # install the named routes in this session instance.
195
+ klass = metaclass
196
+ ActionController::Routing::Routes.install_helpers(klass)
197
+
198
+ # the helpers are made protected by default--we make them public for
199
+ # easier access during testing and troubleshooting.
200
+ klass.module_eval { public *ActionController::Routing::Routes.named_routes.helpers }
201
+ @named_routes_configured = true
202
+ end
203
+ end
204
+
205
+ # Specify whether or not the session should mimic a secure HTTPS request.
206
+ #
207
+ # session.https!
208
+ # session.https!(false)
209
+ def https!(flag = true)
210
+ @https = flag
211
+ end
212
+
213
+ # Return +true+ if the session is mimicking a secure HTTPS request.
214
+ #
215
+ # if session.https?
216
+ # ...
217
+ # end
218
+ def https?
219
+ @https
220
+ end
221
+
222
+ # Set the host name to use in the next request.
223
+ #
224
+ # session.host! "www.example.com"
225
+ def host!(name)
226
+ @host = name
227
+ end
228
+
229
+ # Returns the URL for the given options, according to the rules specified
230
+ # in the application's routes.
231
+ def url_for(options)
232
+ controller ?
233
+ controller.url_for(options) :
234
+ generic_url_rewriter.rewrite(options)
235
+ end
236
+
237
+ private
238
+
239
+ # Performs the actual request.
240
+ def process(method, path, parameters = nil, rack_environment = nil)
241
+ if path =~ %r{://}
242
+ location = URI.parse(path)
243
+ https! URI::HTTPS === location if location.scheme
244
+ host! location.host if location.host
245
+ path = location.query ? "#{location.path}?#{location.query}" : location.path
246
+ end
247
+
248
+ [ControllerCapture, ActionController::Testing].each do |mod|
249
+ unless ActionController::Base < mod
250
+ ActionController::Base.class_eval { include mod }
251
+ end
252
+ end
253
+
254
+ env = {
255
+ :method => method,
256
+ :params => parameters,
257
+
258
+ "SERVER_NAME" => host,
259
+ "SERVER_PORT" => (https? ? "443" : "80"),
260
+ "HTTPS" => https? ? "on" : "off",
261
+ "rack.url_scheme" => https? ? "https" : "http",
262
+
263
+ "REQUEST_URI" => path,
264
+ "HTTP_HOST" => host,
265
+ "REMOTE_ADDR" => remote_addr,
266
+ "CONTENT_TYPE" => "application/x-www-form-urlencoded",
267
+ "HTTP_ACCEPT" => accept
268
+ }
269
+
270
+ (rack_environment || {}).each do |key, value|
271
+ env[key] = value
272
+ end
273
+
274
+ session = Rack::Test::Session.new(@mock_session)
275
+
276
+ @controller = ActionController::Base.capture_instantiation do
277
+ session.request(path, env)
278
+ end
279
+
280
+ @request_count += 1
281
+ @request = ActionDispatch::Request.new(session.last_request.env)
282
+ @response = ActionDispatch::TestResponse.from_response(@mock_session.last_response)
283
+ @html_document = nil
284
+
285
+ return response.status
286
+ end
287
+
288
+ # Get a temporary URL writer object
289
+ def generic_url_rewriter
290
+ env = {
291
+ 'REQUEST_METHOD' => "GET",
292
+ 'QUERY_STRING' => "",
293
+ "REQUEST_URI" => "/",
294
+ "HTTP_HOST" => host,
295
+ "SERVER_PORT" => https? ? "443" : "80",
296
+ "HTTPS" => https? ? "on" : "off"
297
+ }
298
+ ActionController::UrlRewriter.new(ActionDispatch::Request.new(env), {})
299
+ end
300
+ end
301
+
302
+ # A module used to extend ActionController::Base, so that integration tests
303
+ # can capture the controller used to satisfy a request.
304
+ module ControllerCapture #:nodoc:
305
+ extend ActiveSupport::Concern
306
+
307
+ included do
308
+ alias_method_chain :initialize, :capture
309
+ end
310
+
311
+ def initialize_with_capture(*args)
312
+ initialize_without_capture
313
+ self.class.last_instantiation ||= self
314
+ end
315
+
316
+ module ClassMethods #:nodoc:
317
+ mattr_accessor :last_instantiation
318
+
319
+ def capture_instantiation
320
+ self.last_instantiation = nil
321
+ yield
322
+ return last_instantiation
323
+ end
324
+ end
325
+ end
326
+
327
+ module Runner
328
+ def app
329
+ @app
330
+ end
331
+
332
+ # Reset the current session. This is useful for testing multiple sessions
333
+ # in a single test case.
334
+ def reset!
335
+ @integration_session = open_session
336
+ end
337
+
338
+ %w(get post put head delete cookies assigns
339
+ xml_http_request xhr get_via_redirect post_via_redirect).each do |method|
340
+ define_method(method) do |*args|
341
+ reset! unless @integration_session
342
+ # reset the html_document variable, but only for new get/post calls
343
+ @html_document = nil unless %w(cookies assigns).include?(method)
344
+ returning @integration_session.__send__(method, *args) do
345
+ copy_session_variables!
346
+ end
347
+ end
348
+ end
349
+
350
+ # Open a new session instance. If a block is given, the new session is
351
+ # yielded to the block before being returned.
352
+ #
353
+ # session = open_session do |sess|
354
+ # sess.extend(CustomAssertions)
355
+ # end
356
+ #
357
+ # By default, a single session is automatically created for you, but you
358
+ # can use this method to open multiple sessions that ought to be tested
359
+ # simultaneously.
360
+ def open_session(app = nil)
361
+ session = Integration::Session.new(app || self.app)
362
+
363
+ # delegate the fixture accessors back to the test instance
364
+ extras = Module.new { attr_accessor :delegate, :test_result }
365
+ if self.class.respond_to?(:fixture_table_names)
366
+ self.class.fixture_table_names.each do |table_name|
367
+ name = table_name.tr(".", "_")
368
+ next unless respond_to?(name)
369
+ extras.__send__(:define_method, name) { |*args|
370
+ delegate.send(name, *args)
371
+ }
372
+ end
373
+ end
374
+
375
+ # delegate add_assertion to the test case
376
+ extras.__send__(:define_method, :add_assertion) {
377
+ test_result.add_assertion
378
+ }
379
+ session.extend(extras)
380
+ session.delegate = self
381
+ session.test_result = @_result
382
+
383
+ yield session if block_given?
384
+ session
385
+ end
386
+
387
+ # Copy the instance variables from the current session instance into the
388
+ # test instance.
389
+ def copy_session_variables! #:nodoc:
390
+ return unless @integration_session
391
+ %w(controller response request).each do |var|
392
+ instance_variable_set("@#{var}", @integration_session.__send__(var))
393
+ end
394
+ end
395
+
396
+ # Delegate unhandled messages to the current session instance.
397
+ def method_missing(sym, *args, &block)
398
+ reset! unless @integration_session
399
+ if @integration_session.respond_to?(sym)
400
+ returning @integration_session.__send__(sym, *args, &block) do
401
+ copy_session_variables!
402
+ end
403
+ else
404
+ super
405
+ end
406
+ end
407
+ end
408
+ end
409
+
410
+ # An IntegrationTest is one that spans multiple controllers and actions,
411
+ # tying them all together to ensure they work together as expected. It tests
412
+ # more completely than either unit or functional tests do, exercising the
413
+ # entire stack, from the dispatcher to the database.
414
+ #
415
+ # At its simplest, you simply extend IntegrationTest and write your tests
416
+ # using the get/post methods:
417
+ #
418
+ # require "#{File.dirname(__FILE__)}/test_helper"
419
+ #
420
+ # class ExampleTest < ActionController::IntegrationTest
421
+ # fixtures :people
422
+ #
423
+ # def test_login
424
+ # # get the login page
425
+ # get "/login"
426
+ # assert_equal 200, status
427
+ #
428
+ # # post the login and follow through to the home page
429
+ # post "/login", :username => people(:jamis).username,
430
+ # :password => people(:jamis).password
431
+ # follow_redirect!
432
+ # assert_equal 200, status
433
+ # assert_equal "/home", path
434
+ # end
435
+ # end
436
+ #
437
+ # However, you can also have multiple session instances open per test, and
438
+ # even extend those instances with assertions and methods to create a very
439
+ # powerful testing DSL that is specific for your application. You can even
440
+ # reference any named routes you happen to have defined!
441
+ #
442
+ # require "#{File.dirname(__FILE__)}/test_helper"
443
+ #
444
+ # class AdvancedTest < ActionController::IntegrationTest
445
+ # fixtures :people, :rooms
446
+ #
447
+ # def test_login_and_speak
448
+ # jamis, david = login(:jamis), login(:david)
449
+ # room = rooms(:office)
450
+ #
451
+ # jamis.enter(room)
452
+ # jamis.speak(room, "anybody home?")
453
+ #
454
+ # david.enter(room)
455
+ # david.speak(room, "hello!")
456
+ # end
457
+ #
458
+ # private
459
+ #
460
+ # module CustomAssertions
461
+ # def enter(room)
462
+ # # reference a named route, for maximum internal consistency!
463
+ # get(room_url(:id => room.id))
464
+ # assert(...)
465
+ # ...
466
+ # end
467
+ #
468
+ # def speak(room, message)
469
+ # xml_http_request "/say/#{room.id}", :message => message
470
+ # assert(...)
471
+ # ...
472
+ # end
473
+ # end
474
+ #
475
+ # def login(who)
476
+ # open_session do |sess|
477
+ # sess.extend(CustomAssertions)
478
+ # who = people(who)
479
+ # sess.post "/login", :username => who.username,
480
+ # :password => who.password
481
+ # assert(...)
482
+ # end
483
+ # end
484
+ # end
485
+ class IntegrationTest < ActiveSupport::TestCase
486
+ include Integration::Runner
487
+
488
+ @@app = nil
489
+
490
+ def self.app
491
+ # DEPRECATE Rails application fallback
492
+ # This should be set by the initializer
493
+ @@app || (defined?(Rails.application) && Rails.application) || nil
494
+ end
495
+
496
+ def self.app=(app)
497
+ @@app = app
498
+ end
499
+
500
+ def app
501
+ super || self.class.app
502
+ end
503
+ end
504
+ end