actionpack 4.2.11.3 → 5.0.7.2

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 (136) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +890 -384
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -3
  5. data/lib/abstract_controller/base.rb +28 -38
  6. data/lib/{action_controller → abstract_controller}/caching/fragments.rb +51 -11
  7. data/lib/abstract_controller/caching.rb +62 -0
  8. data/lib/abstract_controller/callbacks.rb +54 -19
  9. data/lib/abstract_controller/collector.rb +4 -9
  10. data/lib/abstract_controller/error.rb +4 -0
  11. data/lib/abstract_controller/helpers.rb +4 -3
  12. data/lib/abstract_controller/railties/routes_helpers.rb +2 -2
  13. data/lib/abstract_controller/rendering.rb +28 -18
  14. data/lib/abstract_controller/translation.rb +8 -7
  15. data/lib/abstract_controller.rb +6 -2
  16. data/lib/action_controller/api/api_rendering.rb +14 -0
  17. data/lib/action_controller/api.rb +147 -0
  18. data/lib/action_controller/base.rb +14 -11
  19. data/lib/action_controller/caching.rb +13 -58
  20. data/lib/action_controller/form_builder.rb +48 -0
  21. data/lib/action_controller/log_subscriber.rb +3 -10
  22. data/lib/action_controller/metal/basic_implicit_render.rb +11 -0
  23. data/lib/action_controller/metal/conditional_get.rb +106 -34
  24. data/lib/action_controller/metal/cookies.rb +1 -3
  25. data/lib/action_controller/metal/data_streaming.rb +14 -34
  26. data/lib/action_controller/metal/etag_with_template_digest.rb +8 -2
  27. data/lib/action_controller/metal/exceptions.rb +11 -6
  28. data/lib/action_controller/metal/force_ssl.rb +11 -11
  29. data/lib/action_controller/metal/head.rb +14 -8
  30. data/lib/action_controller/metal/helpers.rb +15 -6
  31. data/lib/action_controller/metal/http_authentication.rb +44 -35
  32. data/lib/action_controller/metal/implicit_render.rb +61 -6
  33. data/lib/action_controller/metal/instrumentation.rb +5 -5
  34. data/lib/action_controller/metal/live.rb +71 -88
  35. data/lib/action_controller/metal/mime_responds.rb +27 -42
  36. data/lib/action_controller/metal/params_wrapper.rb +9 -9
  37. data/lib/action_controller/metal/redirecting.rb +32 -9
  38. data/lib/action_controller/metal/renderers.rb +83 -40
  39. data/lib/action_controller/metal/rendering.rb +38 -6
  40. data/lib/action_controller/metal/request_forgery_protection.rb +126 -48
  41. data/lib/action_controller/metal/rescue.rb +3 -12
  42. data/lib/action_controller/metal/streaming.rb +4 -4
  43. data/lib/action_controller/metal/strong_parameters.rb +527 -134
  44. data/lib/action_controller/metal/testing.rb +1 -12
  45. data/lib/action_controller/metal/url_for.rb +12 -5
  46. data/lib/action_controller/metal.rb +88 -63
  47. data/lib/action_controller/railtie.rb +11 -7
  48. data/lib/action_controller/renderer.rb +113 -0
  49. data/lib/action_controller/template_assertions.rb +9 -0
  50. data/lib/action_controller/test_case.rb +311 -374
  51. data/lib/action_controller.rb +12 -9
  52. data/lib/action_dispatch/http/cache.rb +73 -34
  53. data/lib/action_dispatch/http/filter_parameters.rb +16 -12
  54. data/lib/action_dispatch/http/filter_redirect.rb +7 -8
  55. data/lib/action_dispatch/http/headers.rb +45 -14
  56. data/lib/action_dispatch/http/mime_negotiation.rb +42 -23
  57. data/lib/action_dispatch/http/mime_type.rb +126 -90
  58. data/lib/action_dispatch/http/mime_types.rb +3 -4
  59. data/lib/action_dispatch/http/parameter_filter.rb +19 -9
  60. data/lib/action_dispatch/http/parameters.rb +70 -40
  61. data/lib/action_dispatch/http/request.rb +144 -89
  62. data/lib/action_dispatch/http/response.rb +215 -102
  63. data/lib/action_dispatch/http/upload.rb +6 -2
  64. data/lib/action_dispatch/http/url.rb +117 -8
  65. data/lib/action_dispatch/journey/formatter.rb +47 -30
  66. data/lib/action_dispatch/journey/gtg/transition_table.rb +1 -1
  67. data/lib/action_dispatch/journey/nfa/dot.rb +0 -2
  68. data/lib/action_dispatch/journey/nfa/transition_table.rb +1 -46
  69. data/lib/action_dispatch/journey/nodes/node.rb +14 -4
  70. data/lib/action_dispatch/journey/parser.rb +2 -0
  71. data/lib/action_dispatch/journey/parser_extras.rb +8 -2
  72. data/lib/action_dispatch/journey/path/pattern.rb +38 -42
  73. data/lib/action_dispatch/journey/route.rb +88 -26
  74. data/lib/action_dispatch/journey/router/utils.rb +5 -5
  75. data/lib/action_dispatch/journey/router.rb +8 -10
  76. data/lib/action_dispatch/journey/routes.rb +14 -15
  77. data/lib/action_dispatch/journey/visitors.rb +89 -44
  78. data/lib/action_dispatch/middleware/callbacks.rb +10 -1
  79. data/lib/action_dispatch/middleware/cookies.rb +188 -134
  80. data/lib/action_dispatch/middleware/debug_exceptions.rb +128 -49
  81. data/lib/action_dispatch/middleware/debug_locks.rb +122 -0
  82. data/lib/action_dispatch/middleware/exception_wrapper.rb +21 -21
  83. data/lib/action_dispatch/middleware/executor.rb +19 -0
  84. data/lib/action_dispatch/middleware/flash.rb +66 -45
  85. data/lib/action_dispatch/middleware/params_parser.rb +32 -46
  86. data/lib/action_dispatch/middleware/public_exceptions.rb +2 -2
  87. data/lib/action_dispatch/middleware/reloader.rb +14 -58
  88. data/lib/action_dispatch/middleware/remote_ip.rb +29 -19
  89. data/lib/action_dispatch/middleware/request_id.rb +11 -6
  90. data/lib/action_dispatch/middleware/session/abstract_store.rb +23 -11
  91. data/lib/action_dispatch/middleware/session/cache_store.rb +9 -6
  92. data/lib/action_dispatch/middleware/session/cookie_store.rb +30 -24
  93. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +4 -0
  94. data/lib/action_dispatch/middleware/show_exceptions.rb +11 -9
  95. data/lib/action_dispatch/middleware/ssl.rb +124 -36
  96. data/lib/action_dispatch/middleware/stack.rb +44 -40
  97. data/lib/action_dispatch/middleware/static.rb +51 -35
  98. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
  99. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  100. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
  101. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  102. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  103. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +59 -63
  104. data/lib/action_dispatch/railtie.rb +2 -2
  105. data/lib/action_dispatch/request/session.rb +69 -33
  106. data/lib/action_dispatch/request/utils.rb +51 -19
  107. data/lib/action_dispatch/routing/inspector.rb +32 -43
  108. data/lib/action_dispatch/routing/mapper.rb +515 -348
  109. data/lib/action_dispatch/routing/polymorphic_routes.rb +8 -14
  110. data/lib/action_dispatch/routing/redirection.rb +5 -4
  111. data/lib/action_dispatch/routing/route_set.rb +148 -240
  112. data/lib/action_dispatch/routing/url_for.rb +27 -10
  113. data/lib/action_dispatch/routing.rb +17 -13
  114. data/lib/action_dispatch/testing/assertion_response.rb +45 -0
  115. data/lib/action_dispatch/testing/assertions/response.rb +38 -20
  116. data/lib/action_dispatch/testing/assertions/routing.rb +16 -12
  117. data/lib/action_dispatch/testing/assertions.rb +1 -1
  118. data/lib/action_dispatch/testing/integration.rb +377 -149
  119. data/lib/action_dispatch/testing/request_encoder.rb +53 -0
  120. data/lib/action_dispatch/testing/test_process.rb +24 -20
  121. data/lib/action_dispatch/testing/test_request.rb +22 -31
  122. data/lib/action_dispatch/testing/test_response.rb +12 -4
  123. data/lib/action_dispatch.rb +4 -1
  124. data/lib/action_pack/gem_version.rb +4 -4
  125. data/lib/action_pack.rb +1 -1
  126. metadata +32 -34
  127. data/lib/action_controller/metal/hide_actions.rb +0 -40
  128. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  129. data/lib/action_controller/middleware.rb +0 -39
  130. data/lib/action_controller/model_naming.rb +0 -12
  131. data/lib/action_dispatch/journey/backwards.rb +0 -5
  132. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  133. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  134. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  135. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
  136. /data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
@@ -2,77 +2,66 @@ require 'stringio'
2
2
  require 'uri'
3
3
  require 'active_support/core_ext/kernel/singleton_class'
4
4
  require 'active_support/core_ext/object/try'
5
+ require 'active_support/core_ext/string/strip'
5
6
  require 'rack/test'
6
7
  require 'minitest'
7
8
 
9
+ require 'action_dispatch/testing/request_encoder'
10
+
8
11
  module ActionDispatch
9
12
  module Integration #:nodoc:
10
13
  module RequestHelpers
11
- # Performs a GET request with the given parameters.
12
- #
13
- # - +path+: The URI (as a String) on which you want to perform a GET
14
- # request.
15
- # - +parameters+: The HTTP parameters that you want to pass. This may
16
- # be +nil+,
17
- # a Hash, or a String that is appropriately encoded
18
- # (<tt>application/x-www-form-urlencoded</tt> or
19
- # <tt>multipart/form-data</tt>).
20
- # - +headers_or_env+: Additional headers to pass, as a Hash. The headers will be
21
- # merged into the Rack env hash.
22
- #
23
- # This method returns a Response object, which one can use to
24
- # inspect the details of the response. Furthermore, if this method was
25
- # called from an ActionDispatch::IntegrationTest object, then that
26
- # object's <tt>@response</tt> instance variable will point to the same
27
- # response object.
28
- #
29
- # You can also perform POST, PATCH, PUT, DELETE, and HEAD requests with
30
- # +#post+, +#patch+, +#put+, +#delete+, and +#head+.
31
- def get(path, parameters = nil, headers_or_env = nil)
32
- process :get, path, parameters, headers_or_env
14
+ def get(path, *args)
15
+ process_with_kwargs(:get, path, *args)
33
16
  end
34
17
 
35
- # Performs a POST request with the given parameters. See +#get+ for more
18
+ # Performs a POST request with the given parameters. See +#process+ for more
36
19
  # details.
37
- def post(path, parameters = nil, headers_or_env = nil)
38
- process :post, path, parameters, headers_or_env
20
+ def post(path, *args)
21
+ process_with_kwargs(:post, path, *args)
39
22
  end
40
23
 
41
- # Performs a PATCH request with the given parameters. See +#get+ for more
24
+ # Performs a PATCH request with the given parameters. See +#process+ for more
42
25
  # details.
43
- def patch(path, parameters = nil, headers_or_env = nil)
44
- process :patch, path, parameters, headers_or_env
26
+ def patch(path, *args)
27
+ process_with_kwargs(:patch, path, *args)
45
28
  end
46
29
 
47
- # Performs a PUT request with the given parameters. See +#get+ for more
30
+ # Performs a PUT request with the given parameters. See +#process+ for more
48
31
  # details.
49
- def put(path, parameters = nil, headers_or_env = nil)
50
- process :put, path, parameters, headers_or_env
32
+ def put(path, *args)
33
+ process_with_kwargs(:put, path, *args)
51
34
  end
52
35
 
53
- # Performs a DELETE request with the given parameters. See +#get+ for
36
+ # Performs a DELETE request with the given parameters. See +#process+ for
54
37
  # more details.
55
- def delete(path, parameters = nil, headers_or_env = nil)
56
- process :delete, path, parameters, headers_or_env
38
+ def delete(path, *args)
39
+ process_with_kwargs(:delete, path, *args)
57
40
  end
58
41
 
59
- # Performs a HEAD request with the given parameters. See +#get+ for more
42
+ # Performs a HEAD request with the given parameters. See +#process+ for more
60
43
  # details.
61
- def head(path, parameters = nil, headers_or_env = nil)
62
- process :head, path, parameters, headers_or_env
44
+ def head(path, *args)
45
+ process_with_kwargs(:head, path, *args)
63
46
  end
64
47
 
65
48
  # Performs an XMLHttpRequest request with the given parameters, mirroring
66
- # a request from the Prototype library.
49
+ # an AJAX request made from JavaScript.
67
50
  #
68
51
  # The request_method is +:get+, +:post+, +:patch+, +:put+, +:delete+ or
69
52
  # +:head+; the parameters are +nil+, a hash, or a url-encoded or multipart
70
53
  # string; the headers are a hash.
54
+ #
55
+ # Example:
56
+ #
57
+ # xhr :get, '/feed', since: 201501011400
71
58
  def xml_http_request(request_method, path, parameters = nil, headers_or_env = nil)
72
- headers_or_env ||= {}
73
- headers_or_env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
74
- headers_or_env['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
75
- process(request_method, path, parameters, headers_or_env)
59
+ ActiveSupport::Deprecation.warn(<<-MSG.strip_heredoc)
60
+ `xhr` and `xml_http_request` are deprecated and will be removed in Rails 5.1.
61
+ Switch to e.g. `post comments_path, params: { comment: { body: 'Honey bunny' } }, xhr: true`.
62
+ MSG
63
+
64
+ process(request_method, path, params: parameters, headers: headers_or_env, xhr: true)
76
65
  end
77
66
  alias xhr :xml_http_request
78
67
 
@@ -89,40 +78,53 @@ module ActionDispatch
89
78
  # redirect. Note that the redirects are followed until the response is
90
79
  # not a redirect--this means you may run into an infinite loop if your
91
80
  # redirect loops back to itself.
92
- def request_via_redirect(http_method, path, parameters = nil, headers_or_env = nil)
93
- process(http_method, path, parameters, headers_or_env)
81
+ #
82
+ # Example:
83
+ #
84
+ # request_via_redirect :post, '/welcome',
85
+ # params: { ref_id: 14 },
86
+ # headers: { "X-Test-Header" => "testvalue" }
87
+ def request_via_redirect(http_method, path, *args)
88
+ ActiveSupport::Deprecation.warn('`request_via_redirect` is deprecated and will be removed in Rails 5.1. Please use `follow_redirect!` manually after the request call for the same behavior.')
89
+ process_with_kwargs(http_method, path, *args)
90
+
94
91
  follow_redirect! while redirect?
95
92
  status
96
93
  end
97
94
 
98
95
  # Performs a GET request, following any subsequent redirect.
99
96
  # See +request_via_redirect+ for more information.
100
- def get_via_redirect(path, parameters = nil, headers_or_env = nil)
101
- request_via_redirect(:get, path, parameters, headers_or_env)
97
+ def get_via_redirect(path, *args)
98
+ ActiveSupport::Deprecation.warn('`get_via_redirect` is deprecated and will be removed in Rails 5.1. Please use `follow_redirect!` manually after the request call for the same behavior.')
99
+ request_via_redirect(:get, path, *args)
102
100
  end
103
101
 
104
102
  # Performs a POST request, following any subsequent redirect.
105
103
  # See +request_via_redirect+ for more information.
106
- def post_via_redirect(path, parameters = nil, headers_or_env = nil)
107
- request_via_redirect(:post, path, parameters, headers_or_env)
104
+ def post_via_redirect(path, *args)
105
+ ActiveSupport::Deprecation.warn('`post_via_redirect` is deprecated and will be removed in Rails 5.1. Please use `follow_redirect!` manually after the request call for the same behavior.')
106
+ request_via_redirect(:post, path, *args)
108
107
  end
109
108
 
110
109
  # Performs a PATCH request, following any subsequent redirect.
111
110
  # See +request_via_redirect+ for more information.
112
- def patch_via_redirect(path, parameters = nil, headers_or_env = nil)
113
- request_via_redirect(:patch, path, parameters, headers_or_env)
111
+ def patch_via_redirect(path, *args)
112
+ ActiveSupport::Deprecation.warn('`patch_via_redirect` is deprecated and will be removed in Rails 5.1. Please use `follow_redirect!` manually after the request call for the same behavior.')
113
+ request_via_redirect(:patch, path, *args)
114
114
  end
115
115
 
116
116
  # Performs a PUT request, following any subsequent redirect.
117
117
  # See +request_via_redirect+ for more information.
118
- def put_via_redirect(path, parameters = nil, headers_or_env = nil)
119
- request_via_redirect(:put, path, parameters, headers_or_env)
118
+ def put_via_redirect(path, *args)
119
+ ActiveSupport::Deprecation.warn('`put_via_redirect` is deprecated and will be removed in Rails 5.1. Please use `follow_redirect!` manually after the request call for the same behavior.')
120
+ request_via_redirect(:put, path, *args)
120
121
  end
121
122
 
122
123
  # Performs a DELETE request, following any subsequent redirect.
123
124
  # See +request_via_redirect+ for more information.
124
- def delete_via_redirect(path, parameters = nil, headers_or_env = nil)
125
- request_via_redirect(:delete, path, parameters, headers_or_env)
125
+ def delete_via_redirect(path, *args)
126
+ ActiveSupport::Deprecation.warn('`delete_via_redirect` is deprecated and will be removed in Rails 5.1. Please use `follow_redirect!` manually after the request call for the same behavior.')
127
+ request_via_redirect(:delete, path, *args)
126
128
  end
127
129
  end
128
130
 
@@ -185,15 +187,6 @@ module ActionDispatch
185
187
  super()
186
188
  @app = app
187
189
 
188
- # If the app is a Rails app, make url_helpers available on the session
189
- # This makes app.url_for and app.foo_path available in the console
190
- if app.respond_to?(:routes)
191
- singleton_class.class_eval do
192
- include app.routes.url_helpers
193
- include app.routes.mounted_helpers
194
- end
195
- end
196
-
197
190
  reset!
198
191
  end
199
192
 
@@ -251,6 +244,108 @@ module ActionDispatch
251
244
  @https
252
245
  end
253
246
 
247
+ # Performs the actual request.
248
+ #
249
+ # - +method+: The HTTP method (GET, POST, PATCH, PUT, DELETE, HEAD, OPTIONS)
250
+ # as a symbol.
251
+ # - +path+: The URI (as a String) on which you want to perform the
252
+ # request.
253
+ # - +params+: The HTTP parameters that you want to pass. This may
254
+ # be +nil+,
255
+ # a Hash, or a String that is appropriately encoded
256
+ # (<tt>application/x-www-form-urlencoded</tt> or
257
+ # <tt>multipart/form-data</tt>).
258
+ # - +headers+: Additional headers to pass, as a Hash. The headers will be
259
+ # merged into the Rack env hash.
260
+ # - +env+: Additional env to pass, as a Hash. The headers will be
261
+ # merged into the Rack env hash.
262
+ #
263
+ # This method is rarely used directly. Use +#get+, +#post+, or other standard
264
+ # HTTP methods in integration tests. +#process+ is only required when using a
265
+ # request method that doesn't have a method defined in the integrations tests.
266
+ #
267
+ # This method returns a Response object, which one can use to
268
+ # inspect the details of the response. Furthermore, if this method was
269
+ # called from an ActionDispatch::IntegrationTest object, then that
270
+ # object's <tt>@response</tt> instance variable will point to the same
271
+ # response object.
272
+ #
273
+ # Example:
274
+ #
275
+ # process :get, '/author', params: { since: 201501011400 }
276
+ def process(method, path, params: nil, headers: nil, env: nil, xhr: false, as: nil)
277
+ request_encoder = RequestEncoder.encoder(as)
278
+ headers ||= {}
279
+
280
+ if method == :get && as == :json && params
281
+ headers['X-Http-Method-Override'] = 'GET'
282
+ method = :post
283
+ end
284
+
285
+ if path =~ %r{://}
286
+ location = URI.parse(path)
287
+ https! URI::HTTPS === location if location.scheme
288
+ if url_host = location.host
289
+ default = Rack::Request::DEFAULT_PORTS[location.scheme]
290
+ url_host += ":#{location.port}" if default != location.port
291
+ host! url_host
292
+ end
293
+ path = location.query ? "#{location.path}?#{location.query}" : location.path
294
+ end
295
+
296
+ hostname, port = host.split(':')
297
+
298
+ request_env = {
299
+ :method => method,
300
+ :params => request_encoder.encode_params(params),
301
+
302
+ "SERVER_NAME" => hostname,
303
+ "SERVER_PORT" => port || (https? ? "443" : "80"),
304
+ "HTTPS" => https? ? "on" : "off",
305
+ "rack.url_scheme" => https? ? "https" : "http",
306
+
307
+ "REQUEST_URI" => path,
308
+ "HTTP_HOST" => host,
309
+ "REMOTE_ADDR" => remote_addr,
310
+ "CONTENT_TYPE" => request_encoder.content_type,
311
+ "HTTP_ACCEPT" => request_encoder.accept_header || accept
312
+ }
313
+
314
+ wrapped_headers = Http::Headers.from_hash({})
315
+ wrapped_headers.merge!(headers) if headers
316
+
317
+ if xhr
318
+ wrapped_headers["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest"
319
+ wrapped_headers["HTTP_ACCEPT"] ||= [Mime[:js], Mime[:html], Mime[:xml], "text/xml", "*/*"].join(", ")
320
+ end
321
+
322
+ # this modifies the passed request_env directly
323
+ if wrapped_headers.present?
324
+ Http::Headers.from_hash(request_env).merge!(wrapped_headers)
325
+ end
326
+ if env.present?
327
+ Http::Headers.from_hash(request_env).merge!(env)
328
+ end
329
+
330
+ session = Rack::Test::Session.new(_mock_session)
331
+
332
+ # NOTE: rack-test v0.5 doesn't build a default uri correctly
333
+ # Make sure requested path is always a full uri
334
+ session.request(build_full_uri(path, request_env), request_env)
335
+
336
+ @request_count += 1
337
+ @request = ActionDispatch::Request.new(session.last_request.env)
338
+ response = _mock_session.last_response
339
+ @response = ActionDispatch::TestResponse.from_response(response)
340
+ @response.request = @request
341
+ @html_document = nil
342
+ @url_options = nil
343
+
344
+ @controller = @request.controller_instance
345
+
346
+ response.status
347
+ end
348
+
254
349
  # Set the host name to use in the next request.
255
350
  #
256
351
  # session.host! "www.example.com"
@@ -261,52 +356,31 @@ module ActionDispatch
261
356
  @_mock_session ||= Rack::MockSession.new(@app, host)
262
357
  end
263
358
 
264
- # Performs the actual request.
265
- def process(method, path, parameters = nil, headers_or_env = nil)
266
- if path =~ %r{://}
267
- location = URI.parse(path)
268
- https! URI::HTTPS === location if location.scheme
269
- host! "#{location.host}:#{location.port}" if location.host
270
- path = location.query ? "#{location.path}?#{location.query}" : location.path
359
+ def process_with_kwargs(http_method, path, *args)
360
+ if kwarg_request?(args)
361
+ process(http_method, path, *args)
362
+ else
363
+ non_kwarg_request_warning if args.any?
364
+ process(http_method, path, { params: args[0], headers: args[1] })
271
365
  end
366
+ end
272
367
 
273
- hostname, port = host.split(':')
274
-
275
- env = {
276
- :method => method,
277
- :params => parameters,
278
-
279
- "SERVER_NAME" => hostname,
280
- "SERVER_PORT" => port || (https? ? "443" : "80"),
281
- "HTTPS" => https? ? "on" : "off",
282
- "rack.url_scheme" => https? ? "https" : "http",
283
-
284
- "REQUEST_URI" => path,
285
- "HTTP_HOST" => host,
286
- "REMOTE_ADDR" => remote_addr,
287
- "CONTENT_TYPE" => "application/x-www-form-urlencoded",
288
- "HTTP_ACCEPT" => accept
289
- }
290
- # this modifies the passed env directly
291
- Http::Headers.new(env).merge!(headers_or_env || {})
292
-
293
- session = Rack::Test::Session.new(_mock_session)
294
-
295
- # NOTE: rack-test v0.5 doesn't build a default uri correctly
296
- # Make sure requested path is always a full uri
297
- session.request(build_full_uri(path, env), env)
368
+ REQUEST_KWARGS = %i(params headers env xhr as)
369
+ def kwarg_request?(args)
370
+ args[0].respond_to?(:keys) && args[0].keys.any? { |k| REQUEST_KWARGS.include?(k) }
371
+ end
298
372
 
299
- @request_count += 1
300
- @request = ActionDispatch::Request.new(session.last_request.env)
301
- response = _mock_session.last_response
302
- @response = ActionDispatch::TestResponse.from_response(response)
303
- @html_document = nil
304
- @html_scanner_document = nil
305
- @url_options = nil
373
+ def non_kwarg_request_warning
374
+ ActiveSupport::Deprecation.warn(<<-MSG.strip_heredoc)
375
+ Using positional arguments in integration tests has been deprecated,
376
+ in favor of keyword arguments, and will be removed in Rails 5.1.
306
377
 
307
- @controller = session.last_request.env['action_controller.instance']
378
+ Deprecated style:
379
+ get "/profile", { id: 1 }, { "X-Extra-Header" => "123" }
308
380
 
309
- return response.status
381
+ New keyword style:
382
+ get "/profile", params: { id: 1 }, headers: { "X-Extra-Header" => "123" }
383
+ MSG
310
384
  end
311
385
 
312
386
  def build_full_uri(path, env)
@@ -317,14 +391,40 @@ module ActionDispatch
317
391
  module Runner
318
392
  include ActionDispatch::Assertions
319
393
 
320
- def app
321
- @app ||= nil
394
+ APP_SESSIONS = {}
395
+
396
+ attr_reader :app
397
+
398
+ def initialize(*args, &blk)
399
+ super(*args, &blk)
400
+ @integration_session = nil
401
+ end
402
+
403
+ def before_setup # :nodoc:
404
+ @app = nil
405
+ super
406
+ end
407
+
408
+ def integration_session
409
+ @integration_session ||= create_session(app)
322
410
  end
323
411
 
324
412
  # Reset the current session. This is useful for testing multiple sessions
325
413
  # in a single test case.
326
414
  def reset!
327
- @integration_session = Integration::Session.new(app)
415
+ @integration_session = create_session(app)
416
+ end
417
+
418
+ def create_session(app)
419
+ klass = APP_SESSIONS[app] ||= Class.new(Integration::Session) {
420
+ # If the app is a Rails app, make url_helpers available on the session
421
+ # This makes app.url_for and app.foo_path available in the console
422
+ if app.respond_to?(:routes)
423
+ include app.routes.url_helpers
424
+ include app.routes.mounted_helpers
425
+ end
426
+ }
427
+ klass.new(app)
328
428
  end
329
429
 
330
430
  def remove! # :nodoc:
@@ -332,15 +432,11 @@ module ActionDispatch
332
432
  end
333
433
 
334
434
  %w(get post patch put head delete cookies assigns
335
- xml_http_request xhr get_via_redirect post_via_redirect).each do |method|
435
+ xml_http_request xhr get_via_redirect post_via_redirect follow_redirect!).each do |method|
336
436
  define_method(method) do |*args|
337
- reset! unless integration_session
338
-
339
437
  # reset the html_document variable, except for cookies/assigns calls
340
438
  unless method == 'cookies' || method == 'assigns'
341
439
  @html_document = nil
342
- @html_scanner_document = nil
343
- reset_template_assertion
344
440
  end
345
441
 
346
442
  integration_session.__send__(method, *args).tap do
@@ -361,7 +457,6 @@ module ActionDispatch
361
457
  # simultaneously.
362
458
  def open_session
363
459
  dup.tap do |session|
364
- session.reset!
365
460
  yield session if block_given?
366
461
  end
367
462
  end
@@ -369,19 +464,16 @@ module ActionDispatch
369
464
  # Copy the instance variables from the current session instance into the
370
465
  # test instance.
371
466
  def copy_session_variables! #:nodoc:
372
- return unless integration_session
373
- %w(controller response request).each do |var|
374
- instance_variable_set("@#{var}", @integration_session.__send__(var))
375
- end
467
+ @controller = @integration_session.controller
468
+ @response = @integration_session.response
469
+ @request = @integration_session.request
376
470
  end
377
471
 
378
472
  def default_url_options
379
- reset! unless integration_session
380
473
  integration_session.default_url_options
381
474
  end
382
475
 
383
476
  def default_url_options=(options)
384
- reset! unless integration_session
385
477
  integration_session.default_url_options = options
386
478
  end
387
479
 
@@ -391,7 +483,6 @@ module ActionDispatch
391
483
 
392
484
  # Delegate unhandled messages to the current session instance.
393
485
  def method_missing(sym, *args, &block)
394
- reset! unless integration_session
395
486
  if integration_session.respond_to?(sym)
396
487
  integration_session.__send__(sym, *args, &block).tap do
397
488
  copy_session_variables!
@@ -400,11 +491,6 @@ module ActionDispatch
400
491
  super
401
492
  end
402
493
  end
403
-
404
- private
405
- def integration_session
406
- @integration_session ||= nil
407
- end
408
494
  end
409
495
  end
410
496
 
@@ -427,8 +513,8 @@ module ActionDispatch
427
513
  # assert_equal 200, status
428
514
  #
429
515
  # # post the login and follow through to the home page
430
- # post "/login", username: people(:jamis).username,
431
- # password: people(:jamis).password
516
+ # post "/login", params: { username: people(:jamis).username,
517
+ # password: people(:jamis).password }
432
518
  # follow_redirect!
433
519
  # assert_equal 200, status
434
520
  # assert_equal "/home", path
@@ -467,7 +553,7 @@ module ActionDispatch
467
553
  # end
468
554
  #
469
555
  # def speak(room, message)
470
- # xml_http_request "/say/#{room.id}", message: message
556
+ # post "/say/#{room.id}", xhr: true, params: { message: message }
471
557
  # assert(...)
472
558
  # ...
473
559
  # end
@@ -477,38 +563,180 @@ module ActionDispatch
477
563
  # open_session do |sess|
478
564
  # sess.extend(CustomAssertions)
479
565
  # who = people(who)
480
- # sess.post "/login", username: who.username,
481
- # password: who.password
566
+ # sess.post "/login", params: { username: who.username,
567
+ # password: who.password }
482
568
  # assert(...)
483
569
  # end
484
570
  # end
485
571
  # end
486
- class IntegrationTest < ActiveSupport::TestCase
487
- include Integration::Runner
488
- include ActionController::TemplateAssertions
489
- include ActionDispatch::Routing::UrlFor
572
+ #
573
+ # Another longer example would be:
574
+ #
575
+ # A simple integration test that exercises multiple controllers:
576
+ #
577
+ # require 'test_helper'
578
+ #
579
+ # class UserFlowsTest < ActionDispatch::IntegrationTest
580
+ # test "login and browse site" do
581
+ # # login via https
582
+ # https!
583
+ # get "/login"
584
+ # assert_response :success
585
+ #
586
+ # post "/login", params: { username: users(:david).username, password: users(:david).password }
587
+ # follow_redirect!
588
+ # assert_equal '/welcome', path
589
+ # assert_equal 'Welcome david!', flash[:notice]
590
+ #
591
+ # https!(false)
592
+ # get "/articles/all"
593
+ # assert_response :success
594
+ # assert_select 'h1', 'Articles'
595
+ # end
596
+ # end
597
+ #
598
+ # As you can see the integration test involves multiple controllers and
599
+ # exercises the entire stack from database to dispatcher. In addition you can
600
+ # have multiple session instances open simultaneously in a test and extend
601
+ # those instances with assertion methods to create a very powerful testing
602
+ # DSL (domain-specific language) just for your application.
603
+ #
604
+ # Here's an example of multiple sessions and custom DSL in an integration test
605
+ #
606
+ # require 'test_helper'
607
+ #
608
+ # class UserFlowsTest < ActionDispatch::IntegrationTest
609
+ # test "login and browse site" do
610
+ # # User david logs in
611
+ # david = login(:david)
612
+ # # User guest logs in
613
+ # guest = login(:guest)
614
+ #
615
+ # # Both are now available in different sessions
616
+ # assert_equal 'Welcome david!', david.flash[:notice]
617
+ # assert_equal 'Welcome guest!', guest.flash[:notice]
618
+ #
619
+ # # User david can browse site
620
+ # david.browses_site
621
+ # # User guest can browse site as well
622
+ # guest.browses_site
623
+ #
624
+ # # Continue with other assertions
625
+ # end
626
+ #
627
+ # private
628
+ #
629
+ # module CustomDsl
630
+ # def browses_site
631
+ # get "/products/all"
632
+ # assert_response :success
633
+ # assert_select 'h1', 'Products'
634
+ # end
635
+ # end
636
+ #
637
+ # def login(user)
638
+ # open_session do |sess|
639
+ # sess.extend(CustomDsl)
640
+ # u = users(user)
641
+ # sess.https!
642
+ # sess.post "/login", params: { username: u.username, password: u.password }
643
+ # assert_equal '/welcome', sess.path
644
+ # sess.https!(false)
645
+ # end
646
+ # end
647
+ # end
648
+ #
649
+ # See the {request helpers documentation}[rdoc-ref:ActionDispatch::Integration::RequestHelpers] for help on how to
650
+ # use +get+, etc.
651
+ #
652
+ # === Changing the request encoding
653
+ #
654
+ # You can also test your JSON API easily by setting what the request should
655
+ # be encoded as:
656
+ #
657
+ # require "test_helper"
658
+ #
659
+ # class ApiTest < ActionDispatch::IntegrationTest
660
+ # test "creates articles" do
661
+ # assert_difference -> { Article.count } do
662
+ # post articles_path, params: { article: { title: "Ahoy!" } }, as: :json
663
+ # end
664
+ #
665
+ # assert_response :success
666
+ # assert_equal({ id: Arcticle.last.id, title: "Ahoy!" }, response.parsed_body)
667
+ # end
668
+ # end
669
+ #
670
+ # The +as+ option passes an "application/json" Accept header (thereby setting
671
+ # the request format to JSON unless overridden), sets the content type to
672
+ # "application/json" and encodes the parameters as JSON.
673
+ #
674
+ # Calling +parsed_body+ on the response parses the response body based on the
675
+ # last response MIME type.
676
+ #
677
+ # Out of the box, only <tt>:json</tt> is supported. But for any custom MIME
678
+ # types you've registered, you can add your own encoders with:
679
+ #
680
+ # ActionDispatch::IntegrationTest.register_encoder :wibble,
681
+ # param_encoder: -> params { params.to_wibble },
682
+ # response_parser: -> body { body }
683
+ #
684
+ # Where +param_encoder+ defines how the params should be encoded and
685
+ # +response_parser+ defines how the response body should be parsed through
686
+ # +parsed_body+.
687
+ #
688
+ # Consult the Rails Testing Guide for more.
490
689
 
491
- @@app = nil
690
+ class IntegrationTest < ActiveSupport::TestCase
691
+ include TestProcess::FixtureFile
492
692
 
493
- def self.app
494
- @@app || ActionDispatch.test_app
693
+ module UrlOptions
694
+ extend ActiveSupport::Concern
695
+ def url_options
696
+ integration_session.url_options
697
+ end
495
698
  end
496
699
 
497
- def self.app=(app)
498
- @@app = app
499
- end
700
+ module Behavior
701
+ extend ActiveSupport::Concern
500
702
 
501
- def app
502
- super || self.class.app
503
- end
703
+ include Integration::Runner
704
+ include ActionController::TemplateAssertions
504
705
 
505
- def url_options
506
- reset! unless integration_session
507
- integration_session.url_options
508
- end
706
+ included do
707
+ include ActionDispatch::Routing::UrlFor
708
+ include UrlOptions # don't let UrlFor override the url_options method
709
+ ActiveSupport.run_load_hooks(:action_dispatch_integration_test, self)
710
+ @@app = nil
711
+ end
509
712
 
510
- def document_root_element
511
- html_document.root
713
+ module ClassMethods
714
+ def app
715
+ if defined?(@@app) && @@app
716
+ @@app
717
+ else
718
+ ActionDispatch.test_app
719
+ end
720
+ end
721
+
722
+ def app=(app)
723
+ @@app = app
724
+ end
725
+
726
+ def register_encoder(*args)
727
+ RequestEncoder.register_encoder(*args)
728
+ end
729
+ end
730
+
731
+ def app
732
+ super || self.class.app
733
+ end
734
+
735
+ def document_root_element
736
+ html_document.root
737
+ end
512
738
  end
739
+
740
+ include Behavior
513
741
  end
514
742
  end