actionpack 5.2.8.1 → 6.0.6
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.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +270 -347
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -3
- data/lib/abstract_controller/base.rb +4 -3
- data/lib/abstract_controller/caching/fragments.rb +6 -22
- data/lib/abstract_controller/caching.rb +1 -1
- data/lib/abstract_controller/callbacks.rb +12 -0
- data/lib/abstract_controller/collector.rb +1 -2
- data/lib/abstract_controller/helpers.rb +7 -6
- data/lib/abstract_controller/railties/routes_helpers.rb +1 -1
- data/lib/abstract_controller/translation.rb +4 -4
- data/lib/action_controller/api.rb +2 -1
- data/lib/action_controller/base.rb +2 -7
- data/lib/action_controller/caching.rb +1 -2
- data/lib/action_controller/log_subscriber.rb +8 -5
- data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
- data/lib/action_controller/metal/conditional_get.rb +9 -3
- data/lib/action_controller/metal/content_security_policy.rb +0 -1
- data/lib/action_controller/metal/data_streaming.rb +5 -6
- data/lib/action_controller/metal/default_headers.rb +17 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +1 -1
- data/lib/action_controller/metal/exceptions.rb +23 -2
- data/lib/action_controller/metal/flash.rb +5 -5
- data/lib/action_controller/metal/force_ssl.rb +15 -56
- data/lib/action_controller/metal/head.rb +1 -1
- data/lib/action_controller/metal/helpers.rb +3 -4
- data/lib/action_controller/metal/http_authentication.rb +20 -21
- data/lib/action_controller/metal/implicit_render.rb +4 -14
- data/lib/action_controller/metal/instrumentation.rb +3 -6
- data/lib/action_controller/metal/live.rb +29 -31
- data/lib/action_controller/metal/mime_responds.rb +13 -2
- data/lib/action_controller/metal/params_wrapper.rb +18 -14
- data/lib/action_controller/metal/redirecting.rb +5 -5
- data/lib/action_controller/metal/renderers.rb +4 -4
- data/lib/action_controller/metal/rendering.rb +2 -3
- data/lib/action_controller/metal/request_forgery_protection.rb +25 -48
- data/lib/action_controller/metal/streaming.rb +0 -1
- data/lib/action_controller/metal/strong_parameters.rb +65 -44
- data/lib/action_controller/metal/url_for.rb +1 -1
- data/lib/action_controller/metal.rb +8 -6
- data/lib/action_controller/railties/helpers.rb +1 -1
- data/lib/action_controller/renderer.rb +17 -3
- data/lib/action_controller/template_assertions.rb +1 -1
- data/lib/action_controller/test_case.rb +7 -8
- data/lib/action_controller.rb +5 -1
- data/lib/action_dispatch/http/cache.rb +14 -11
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +28 -17
- data/lib/action_dispatch/http/filter_parameters.rb +8 -7
- data/lib/action_dispatch/http/filter_redirect.rb +1 -2
- data/lib/action_dispatch/http/headers.rb +1 -2
- data/lib/action_dispatch/http/mime_negotiation.rb +13 -6
- data/lib/action_dispatch/http/mime_type.rb +14 -8
- data/lib/action_dispatch/http/parameter_filter.rb +5 -79
- data/lib/action_dispatch/http/parameters.rb +15 -6
- data/lib/action_dispatch/http/request.rb +21 -14
- data/lib/action_dispatch/http/response.rb +40 -21
- data/lib/action_dispatch/http/upload.rb +9 -1
- data/lib/action_dispatch/http/url.rb +81 -82
- data/lib/action_dispatch/journey/formatter.rb +2 -3
- data/lib/action_dispatch/journey/gtg/builder.rb +0 -1
- data/lib/action_dispatch/journey/gtg/transition_table.rb +0 -1
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -2
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -1
- data/lib/action_dispatch/journey/nodes/node.rb +9 -8
- data/lib/action_dispatch/journey/path/pattern.rb +6 -3
- data/lib/action_dispatch/journey/route.rb +5 -4
- data/lib/action_dispatch/journey/router/utils.rb +10 -10
- data/lib/action_dispatch/journey/router.rb +0 -4
- data/lib/action_dispatch/journey/routes.rb +0 -2
- data/lib/action_dispatch/journey/scanner.rb +10 -4
- data/lib/action_dispatch/journey/visitors.rb +1 -4
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
- data/lib/action_dispatch/middleware/callbacks.rb +2 -4
- data/lib/action_dispatch/middleware/cookies.rb +62 -78
- data/lib/action_dispatch/middleware/debug_exceptions.rb +45 -61
- data/lib/action_dispatch/middleware/debug_locks.rb +5 -5
- data/lib/action_dispatch/middleware/debug_view.rb +66 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +49 -16
- data/lib/action_dispatch/middleware/flash.rb +1 -1
- data/lib/action_dispatch/middleware/host_authorization.rb +121 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +6 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +9 -12
- data/lib/action_dispatch/middleware/request_id.rb +2 -2
- data/lib/action_dispatch/middleware/session/abstract_store.rb +0 -1
- data/lib/action_dispatch/middleware/session/cookie_store.rb +1 -7
- data/lib/action_dispatch/middleware/show_exceptions.rb +1 -2
- data/lib/action_dispatch/middleware/ssl.rb +8 -8
- data/lib/action_dispatch/middleware/stack.rb +38 -2
- data/lib/action_dispatch/middleware/static.rb +6 -7
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
- data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +3 -1
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +4 -2
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +26 -4
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +7 -4
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +5 -2
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +4 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +3 -0
- data/lib/action_dispatch/railtie.rb +7 -2
- data/lib/action_dispatch/request/session.rb +9 -2
- data/lib/action_dispatch/routing/inspector.rb +97 -50
- data/lib/action_dispatch/routing/mapper.rb +63 -42
- data/lib/action_dispatch/routing/polymorphic_routes.rb +3 -6
- data/lib/action_dispatch/routing/route_set.rb +25 -31
- data/lib/action_dispatch/routing/url_for.rb +2 -2
- data/lib/action_dispatch/routing.rb +21 -20
- data/lib/action_dispatch/system_test_case.rb +44 -6
- data/lib/action_dispatch/system_testing/browser.rb +38 -7
- data/lib/action_dispatch/system_testing/driver.rb +11 -2
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +6 -5
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +7 -6
- data/lib/action_dispatch/testing/assertion_response.rb +0 -1
- data/lib/action_dispatch/testing/assertions/response.rb +2 -3
- data/lib/action_dispatch/testing/assertions/routing.rb +15 -3
- data/lib/action_dispatch/testing/assertions.rb +1 -1
- data/lib/action_dispatch/testing/integration.rb +33 -12
- data/lib/action_dispatch/testing/request_encoder.rb +2 -2
- data/lib/action_dispatch/testing/test_process.rb +2 -2
- data/lib/action_dispatch/testing/test_response.rb +4 -32
- data/lib/action_dispatch.rb +7 -2
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack.rb +1 -1
- metadata +29 -15
- data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +0 -26
@@ -120,8 +120,7 @@ module ActionDispatch
|
|
120
120
|
opts
|
121
121
|
end
|
122
122
|
|
123
|
-
# Returns the path component of a URL for the given record.
|
124
|
-
# <tt>polymorphic_url</tt> with <tt>routing_type: :path</tt>.
|
123
|
+
# Returns the path component of a URL for the given record.
|
125
124
|
def polymorphic_path(record_or_hash_or_array, options = {})
|
126
125
|
if Hash === record_or_hash_or_array
|
127
126
|
options = record_or_hash_or_array.merge(options)
|
@@ -157,7 +156,6 @@ module ActionDispatch
|
|
157
156
|
end
|
158
157
|
|
159
158
|
private
|
160
|
-
|
161
159
|
def polymorphic_url_for_action(action, record_or_hash, options)
|
162
160
|
polymorphic_url(record_or_hash, options.merge(action: action))
|
163
161
|
end
|
@@ -182,8 +180,8 @@ module ActionDispatch
|
|
182
180
|
CACHE[type].fetch(action) { build action, type }
|
183
181
|
end
|
184
182
|
|
185
|
-
def self.url; CACHE["url"
|
186
|
-
def self.path; CACHE["path"
|
183
|
+
def self.url; CACHE["url"][nil]; end
|
184
|
+
def self.path; CACHE["path"][nil]; end
|
187
185
|
|
188
186
|
def self.build(action, type)
|
189
187
|
prefix = action ? "#{action}_" : ""
|
@@ -328,7 +326,6 @@ module ActionDispatch
|
|
328
326
|
end
|
329
327
|
|
330
328
|
private
|
331
|
-
|
332
329
|
def polymorphic_mapping(target, record)
|
333
330
|
if record.respond_to?(:to_model)
|
334
331
|
target._routes.polymorphic_mappings[record.to_model.model_name.name]
|
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
require "action_dispatch/journey"
|
4
4
|
require "active_support/core_ext/object/to_query"
|
5
|
-
require "active_support/core_ext/hash/slice"
|
6
5
|
require "active_support/core_ext/module/redefine_method"
|
7
6
|
require "active_support/core_ext/module/remove_method"
|
8
7
|
require "active_support/core_ext/array/extract_options"
|
@@ -36,12 +35,11 @@ module ActionDispatch
|
|
36
35
|
if @raise_on_name_error
|
37
36
|
raise
|
38
37
|
else
|
39
|
-
|
38
|
+
[404, { "X-Cascade" => "pass" }, []]
|
40
39
|
end
|
41
40
|
end
|
42
41
|
|
43
42
|
private
|
44
|
-
|
45
43
|
def controller(req)
|
46
44
|
req.controller_class
|
47
45
|
rescue NameError => e
|
@@ -60,7 +58,6 @@ module ActionDispatch
|
|
60
58
|
end
|
61
59
|
|
62
60
|
private
|
63
|
-
|
64
61
|
def controller(_); @controller_class; end
|
65
62
|
end
|
66
63
|
|
@@ -91,11 +88,11 @@ module ActionDispatch
|
|
91
88
|
|
92
89
|
def clear!
|
93
90
|
@path_helpers.each do |helper|
|
94
|
-
@path_helpers_module.
|
91
|
+
@path_helpers_module.remove_method helper
|
95
92
|
end
|
96
93
|
|
97
94
|
@url_helpers.each do |helper|
|
98
|
-
@url_helpers_module.
|
95
|
+
@url_helpers_module.remove_method helper
|
99
96
|
end
|
100
97
|
|
101
98
|
@routes.clear
|
@@ -109,8 +106,8 @@ module ActionDispatch
|
|
109
106
|
url_name = :"#{name}_url"
|
110
107
|
|
111
108
|
if routes.key? key
|
112
|
-
@path_helpers_module.
|
113
|
-
@url_helpers_module.
|
109
|
+
@path_helpers_module.undef_method path_name
|
110
|
+
@url_helpers_module.undef_method url_name
|
114
111
|
end
|
115
112
|
routes[key] = route
|
116
113
|
define_url_helper @path_helpers_module, route, path_name, route.defaults, name, PATH
|
@@ -154,13 +151,13 @@ module ActionDispatch
|
|
154
151
|
url_name = :"#{name}_url"
|
155
152
|
|
156
153
|
@path_helpers_module.module_eval do
|
157
|
-
|
154
|
+
redefine_method(path_name) do |*args|
|
158
155
|
helper.call(self, args, true)
|
159
156
|
end
|
160
157
|
end
|
161
158
|
|
162
159
|
@url_helpers_module.module_eval do
|
163
|
-
|
160
|
+
redefine_method(url_name) do |*args|
|
164
161
|
helper.call(self, args, false)
|
165
162
|
end
|
166
163
|
end
|
@@ -216,7 +213,6 @@ module ActionDispatch
|
|
216
213
|
end
|
217
214
|
|
218
215
|
private
|
219
|
-
|
220
216
|
def optimized_helper(args)
|
221
217
|
params = parameterize_args(args) do
|
222
218
|
raise_generation_error(args)
|
@@ -246,7 +242,7 @@ module ActionDispatch
|
|
246
242
|
missing_keys << missing_key
|
247
243
|
}
|
248
244
|
constraints = Hash[@route.requirements.merge(params).sort_by { |k, v| k.to_s }]
|
249
|
-
message = "No route matches #{constraints.inspect}"
|
245
|
+
message = +"No route matches #{constraints.inspect}"
|
250
246
|
message << ", missing required keys: #{missing_keys.sort.inspect}"
|
251
247
|
|
252
248
|
raise ActionController::UrlGenerationError, message
|
@@ -318,23 +314,21 @@ module ActionDispatch
|
|
318
314
|
#
|
319
315
|
def define_url_helper(mod, route, name, opts, route_key, url_strategy)
|
320
316
|
helper = UrlHelper.create(route, opts, route_key, url_strategy)
|
321
|
-
mod.
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
helper.call self, args, options
|
332
|
-
end
|
317
|
+
mod.define_method(name) do |*args|
|
318
|
+
last = args.last
|
319
|
+
options = \
|
320
|
+
case last
|
321
|
+
when Hash
|
322
|
+
args.pop
|
323
|
+
when ActionController::Parameters
|
324
|
+
args.pop.to_h
|
325
|
+
end
|
326
|
+
helper.call self, args, options
|
333
327
|
end
|
334
328
|
end
|
335
329
|
end
|
336
330
|
|
337
|
-
# strategy for building
|
331
|
+
# strategy for building URLs to send to the client
|
338
332
|
PATH = ->(options) { ActionDispatch::Http::URL.path_for(options) }
|
339
333
|
UNKNOWN = ->(options) { ActionDispatch::Http::URL.url_for(options) }
|
340
334
|
|
@@ -378,7 +372,7 @@ module ActionDispatch
|
|
378
372
|
@prepend = []
|
379
373
|
@disable_clear_and_finalize = false
|
380
374
|
@finalized = false
|
381
|
-
@env_key = "ROUTES_#{object_id}_SCRIPT_NAME"
|
375
|
+
@env_key = "ROUTES_#{object_id}_SCRIPT_NAME"
|
382
376
|
|
383
377
|
@set = Journey::Routes.new
|
384
378
|
@router = Journey::Router.new @set
|
@@ -585,7 +579,7 @@ module ActionDispatch
|
|
585
579
|
"You may have defined two routes with the same name using the `:as` option, or " \
|
586
580
|
"you may be overriding a route already defined by a resource with the same naming. " \
|
587
581
|
"For the latter, you can restrict the routes created with `resources` as explained here: \n" \
|
588
|
-
"
|
582
|
+
"https://guides.rubyonrails.org/routing.html#restricting-the-routes-created"
|
589
583
|
end
|
590
584
|
|
591
585
|
route = @set.add_route(name, mapping)
|
@@ -594,14 +588,14 @@ module ActionDispatch
|
|
594
588
|
if route.segment_keys.include?(:controller)
|
595
589
|
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
596
590
|
Using a dynamic :controller segment in a route is deprecated and
|
597
|
-
will be removed in Rails 6.
|
591
|
+
will be removed in Rails 6.1.
|
598
592
|
MSG
|
599
593
|
end
|
600
594
|
|
601
595
|
if route.segment_keys.include?(:action)
|
602
596
|
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
603
597
|
Using a dynamic :action segment in a route is deprecated and
|
604
|
-
will be removed in Rails 6.
|
598
|
+
will be removed in Rails 6.1.
|
605
599
|
MSG
|
606
600
|
end
|
607
601
|
|
@@ -730,7 +724,7 @@ module ActionDispatch
|
|
730
724
|
# Remove leading slashes from controllers
|
731
725
|
def normalize_controller!
|
732
726
|
if controller
|
733
|
-
if controller.start_with?("/"
|
727
|
+
if controller.start_with?("/")
|
734
728
|
@options[:controller] = controller[1..-1]
|
735
729
|
else
|
736
730
|
@options[:controller] = controller
|
@@ -842,7 +836,7 @@ module ActionDispatch
|
|
842
836
|
|
843
837
|
def recognize_path(path, environment = {})
|
844
838
|
method = (environment[:method] || "GET").to_s.upcase
|
845
|
-
path = Journey::Router::Utils.normalize_path(path) unless
|
839
|
+
path = Journey::Router::Utils.normalize_path(path) unless %r{://}.match?(path)
|
846
840
|
extras = environment[:extras] || {}
|
847
841
|
|
848
842
|
begin
|
@@ -107,6 +107,7 @@ module ActionDispatch
|
|
107
107
|
@_routes = nil
|
108
108
|
super
|
109
109
|
end
|
110
|
+
ruby2_keywords(:initialize) if respond_to?(:ruby2_keywords, true)
|
110
111
|
|
111
112
|
# Hook overridden in controller to add request information
|
112
113
|
# with +default_url_options+. Application logic should not
|
@@ -133,6 +134,7 @@ module ActionDispatch
|
|
133
134
|
# <tt>ActionDispatch::Http::URL.tld_length</tt>, which in turn defaults to 1.
|
134
135
|
# * <tt>:port</tt> - Optionally specify the port to connect to.
|
135
136
|
# * <tt>:anchor</tt> - An anchor name to be appended to the path.
|
137
|
+
# * <tt>:params</tt> - The query parameters to be appended to the path.
|
136
138
|
# * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2009/"
|
137
139
|
# * <tt>:script_name</tt> - Specifies application path relative to domain root. If provided, prepends application path.
|
138
140
|
#
|
@@ -214,13 +216,11 @@ module ActionDispatch
|
|
214
216
|
end
|
215
217
|
|
216
218
|
protected
|
217
|
-
|
218
219
|
def optimize_routes_generation?
|
219
220
|
_routes.optimize_routes_generation? && default_url_options.empty?
|
220
221
|
end
|
221
222
|
|
222
223
|
private
|
223
|
-
|
224
224
|
def _with_routes(routes) # :doc:
|
225
225
|
old_routes, @_routes = @_routes, routes
|
226
226
|
yield
|
@@ -74,8 +74,8 @@ module ActionDispatch
|
|
74
74
|
# For routes that don't fit the <tt>resources</tt> mold, you can use the HTTP helper
|
75
75
|
# methods <tt>get</tt>, <tt>post</tt>, <tt>patch</tt>, <tt>put</tt> and <tt>delete</tt>.
|
76
76
|
#
|
77
|
-
# get 'post/:id'
|
78
|
-
# post 'post/:id'
|
77
|
+
# get 'post/:id', to: 'posts#show'
|
78
|
+
# post 'post/:id', to: 'posts#create_comment'
|
79
79
|
#
|
80
80
|
# Now, if you POST to <tt>/posts/:id</tt>, it will route to the <tt>create_comment</tt> action. A GET on the same
|
81
81
|
# URL will route to the <tt>show</tt> action.
|
@@ -83,7 +83,7 @@ module ActionDispatch
|
|
83
83
|
# If your route needs to respond to more than one HTTP method (or all methods) then using the
|
84
84
|
# <tt>:via</tt> option on <tt>match</tt> is preferable.
|
85
85
|
#
|
86
|
-
# match 'post/:id'
|
86
|
+
# match 'post/:id', to: 'posts#show', via: [:get, :post]
|
87
87
|
#
|
88
88
|
# == Named routes
|
89
89
|
#
|
@@ -94,7 +94,7 @@ module ActionDispatch
|
|
94
94
|
# Example:
|
95
95
|
#
|
96
96
|
# # In config/routes.rb
|
97
|
-
# get '/login'
|
97
|
+
# get '/login', to: 'accounts#login', as: 'login'
|
98
98
|
#
|
99
99
|
# # With render, redirect_to, tests, etc.
|
100
100
|
# redirect_to login_url
|
@@ -120,9 +120,9 @@ module ActionDispatch
|
|
120
120
|
#
|
121
121
|
# # In config/routes.rb
|
122
122
|
# controller :blog do
|
123
|
-
# get 'blog/show'
|
124
|
-
# get 'blog/delete'
|
125
|
-
# get 'blog/edit'
|
123
|
+
# get 'blog/show', to: :list
|
124
|
+
# get 'blog/delete', to: :delete
|
125
|
+
# get 'blog/edit', to: :edit
|
126
126
|
# end
|
127
127
|
#
|
128
128
|
# # provides named routes for show, delete, and edit
|
@@ -132,7 +132,7 @@ module ActionDispatch
|
|
132
132
|
#
|
133
133
|
# Routes can generate pretty URLs. For example:
|
134
134
|
#
|
135
|
-
# get '/articles/:year/:month/:day'
|
135
|
+
# get '/articles/:year/:month/:day', to: 'articles#find_by_id', constraints: {
|
136
136
|
# year: /\d{4}/,
|
137
137
|
# month: /\d{1,2}/,
|
138
138
|
# day: /\d{1,2}/
|
@@ -147,7 +147,7 @@ module ActionDispatch
|
|
147
147
|
# You can specify a regular expression to define a format for a parameter.
|
148
148
|
#
|
149
149
|
# controller 'geocode' do
|
150
|
-
# get 'geocode/:postalcode'
|
150
|
+
# get 'geocode/:postalcode', to: :show, constraints: {
|
151
151
|
# postalcode: /\d{5}(-\d{4})?/
|
152
152
|
# }
|
153
153
|
# end
|
@@ -156,13 +156,13 @@ module ActionDispatch
|
|
156
156
|
# expression modifiers:
|
157
157
|
#
|
158
158
|
# controller 'geocode' do
|
159
|
-
# get 'geocode/:postalcode'
|
159
|
+
# get 'geocode/:postalcode', to: :show, constraints: {
|
160
160
|
# postalcode: /hx\d\d\s\d[a-z]{2}/i
|
161
161
|
# }
|
162
162
|
# end
|
163
163
|
#
|
164
164
|
# controller 'geocode' do
|
165
|
-
# get 'geocode/:postalcode'
|
165
|
+
# get 'geocode/:postalcode', to: :show, constraints: {
|
166
166
|
# postalcode: /# Postalcode format
|
167
167
|
# \d{5} #Prefix
|
168
168
|
# (-\d{4})? #Suffix
|
@@ -178,13 +178,13 @@ module ActionDispatch
|
|
178
178
|
#
|
179
179
|
# You can redirect any path to another path using the redirect helper in your router:
|
180
180
|
#
|
181
|
-
# get "/stories"
|
181
|
+
# get "/stories", to: redirect("/posts")
|
182
182
|
#
|
183
183
|
# == Unicode character routes
|
184
184
|
#
|
185
185
|
# You can specify unicode character routes in your router:
|
186
186
|
#
|
187
|
-
# get "こんにちは"
|
187
|
+
# get "こんにちは", to: "welcome#index"
|
188
188
|
#
|
189
189
|
# == Routing to Rack Applications
|
190
190
|
#
|
@@ -192,7 +192,7 @@ module ActionDispatch
|
|
192
192
|
# index action in the PostsController, you can specify any Rack application
|
193
193
|
# as the endpoint for a matcher:
|
194
194
|
#
|
195
|
-
# get "/application.js"
|
195
|
+
# get "/application.js", to: Sprockets
|
196
196
|
#
|
197
197
|
# == Reloading routes
|
198
198
|
#
|
@@ -210,8 +210,8 @@ module ActionDispatch
|
|
210
210
|
# === +assert_routing+
|
211
211
|
#
|
212
212
|
# def test_movie_route_properly_splits
|
213
|
-
#
|
214
|
-
#
|
213
|
+
# opts = {controller: "plugin", action: "checkout", id: "2"}
|
214
|
+
# assert_routing "plugin/checkout/2", opts
|
215
215
|
# end
|
216
216
|
#
|
217
217
|
# +assert_routing+ lets you test whether or not the route properly resolves into options.
|
@@ -219,8 +219,8 @@ module ActionDispatch
|
|
219
219
|
# === +assert_recognizes+
|
220
220
|
#
|
221
221
|
# def test_route_has_options
|
222
|
-
#
|
223
|
-
#
|
222
|
+
# opts = {controller: "plugin", action: "show", id: "12"}
|
223
|
+
# assert_recognizes opts, "/plugins/show/12"
|
224
224
|
# end
|
225
225
|
#
|
226
226
|
# Note the subtle difference between the two: +assert_routing+ tests that
|
@@ -243,8 +243,9 @@ module ActionDispatch
|
|
243
243
|
#
|
244
244
|
# rails routes
|
245
245
|
#
|
246
|
-
# Target specific
|
247
|
-
#
|
246
|
+
# Target a specific controller with <tt>-c</tt>, or grep routes
|
247
|
+
# using <tt>-g</tt>. Useful in conjunction with <tt>--expanded</tt>
|
248
|
+
# which displays routes vertically.
|
248
249
|
module Routing
|
249
250
|
extend ActiveSupport::Autoload
|
250
251
|
|
@@ -4,13 +4,13 @@ gem "capybara", ">= 2.15"
|
|
4
4
|
|
5
5
|
require "capybara/dsl"
|
6
6
|
require "capybara/minitest"
|
7
|
+
require "selenium/webdriver"
|
7
8
|
require "action_controller"
|
8
9
|
require "action_dispatch/system_testing/driver"
|
9
10
|
require "action_dispatch/system_testing/browser"
|
10
11
|
require "action_dispatch/system_testing/server"
|
11
12
|
require "action_dispatch/system_testing/test_helpers/screenshot_helper"
|
12
13
|
require "action_dispatch/system_testing/test_helpers/setup_and_teardown"
|
13
|
-
require "action_dispatch/system_testing/test_helpers/undef_methods"
|
14
14
|
|
15
15
|
module ActionDispatch
|
16
16
|
# = System Testing
|
@@ -89,19 +89,49 @@ module ActionDispatch
|
|
89
89
|
# { js_errors: true }
|
90
90
|
# end
|
91
91
|
#
|
92
|
+
# Some drivers require browser capabilities to be passed as a block instead
|
93
|
+
# of through the +options+ hash.
|
94
|
+
#
|
95
|
+
# As an example, if you want to add mobile emulation on chrome, you'll have to
|
96
|
+
# create an instance of selenium's +Chrome::Options+ object and add
|
97
|
+
# capabilities with a block.
|
98
|
+
#
|
99
|
+
# The block will be passed an instance of <tt><Driver>::Options</tt> where you can
|
100
|
+
# define the capabilities you want. Please refer to your driver documentation
|
101
|
+
# to learn about supported options.
|
102
|
+
#
|
103
|
+
# class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
|
104
|
+
# driven_by :selenium, using: :chrome, screen_size: [1024, 768] do |driver_option|
|
105
|
+
# driver_option.add_emulation(device_name: 'iPhone 6')
|
106
|
+
# driver_option.add_extension('path/to/chrome_extension.crx')
|
107
|
+
# end
|
108
|
+
# end
|
109
|
+
#
|
92
110
|
# Because <tt>ActionDispatch::SystemTestCase</tt> is a shim between Capybara
|
93
111
|
# and Rails, any driver that is supported by Capybara is supported by system
|
94
112
|
# tests as long as you include the required gems and files.
|
95
|
-
class SystemTestCase <
|
113
|
+
class SystemTestCase < ActiveSupport::TestCase
|
96
114
|
include Capybara::DSL
|
97
115
|
include Capybara::Minitest::Assertions
|
98
116
|
include SystemTesting::TestHelpers::SetupAndTeardown
|
99
117
|
include SystemTesting::TestHelpers::ScreenshotHelper
|
100
|
-
include SystemTesting::TestHelpers::UndefMethods
|
101
118
|
|
102
119
|
def initialize(*) # :nodoc:
|
103
120
|
super
|
121
|
+
self.class.driven_by(:selenium) unless self.class.driver?
|
104
122
|
self.class.driver.use
|
123
|
+
@proxy_route = if ActionDispatch.test_app
|
124
|
+
Class.new do
|
125
|
+
include ActionDispatch.test_app.routes.url_helpers
|
126
|
+
include ActionDispatch.test_app.routes.mounted_helpers
|
127
|
+
|
128
|
+
def url_options
|
129
|
+
default_url_options.merge(host: Capybara.app_host)
|
130
|
+
end
|
131
|
+
end.new
|
132
|
+
else
|
133
|
+
nil
|
134
|
+
end
|
105
135
|
end
|
106
136
|
|
107
137
|
def self.start_application # :nodoc:
|
@@ -134,11 +164,19 @@ module ActionDispatch
|
|
134
164
|
# driven_by :selenium, using: :firefox
|
135
165
|
#
|
136
166
|
# driven_by :selenium, using: :headless_firefox
|
137
|
-
def self.driven_by(driver, using: :chrome, screen_size: [1400, 1400], options: {})
|
138
|
-
|
167
|
+
def self.driven_by(driver, using: :chrome, screen_size: [1400, 1400], options: {}, &capabilities)
|
168
|
+
driver_options = { using: using, screen_size: screen_size, options: options }
|
169
|
+
|
170
|
+
self.driver = SystemTesting::Driver.new(driver, **driver_options, &capabilities)
|
139
171
|
end
|
140
172
|
|
141
|
-
|
173
|
+
def method_missing(method, *args, &block)
|
174
|
+
if @proxy_route.respond_to?(method)
|
175
|
+
@proxy_route.send(method, *args, &block)
|
176
|
+
else
|
177
|
+
super
|
178
|
+
end
|
179
|
+
end
|
142
180
|
|
143
181
|
ActiveSupport.run_load_hooks(:action_dispatch_system_test_case, self)
|
144
182
|
end
|
@@ -29,20 +29,51 @@ module ActionDispatch
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
+
def capabilities
|
33
|
+
@option ||=
|
34
|
+
case type
|
35
|
+
when :chrome
|
36
|
+
::Selenium::WebDriver::Chrome::Options.new
|
37
|
+
when :firefox
|
38
|
+
::Selenium::WebDriver::Firefox::Options.new
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# driver_path can be configured as a proc. The webdrivers gem uses this
|
43
|
+
# proc to update web drivers. Running this proc early allows us to only
|
44
|
+
# update the webdriver once and avoid race conditions when using
|
45
|
+
# parallel tests.
|
46
|
+
def preload
|
47
|
+
case type
|
48
|
+
when :chrome
|
49
|
+
if ::Selenium::WebDriver::Service.respond_to? :driver_path=
|
50
|
+
::Selenium::WebDriver::Chrome::Service.driver_path.try(:call)
|
51
|
+
else
|
52
|
+
# Selenium <= v3.141.0
|
53
|
+
::Selenium::WebDriver::Chrome.driver_path
|
54
|
+
end
|
55
|
+
when :firefox
|
56
|
+
if ::Selenium::WebDriver::Service.respond_to? :driver_path=
|
57
|
+
::Selenium::WebDriver::Firefox::Service.driver_path.try(:call)
|
58
|
+
else
|
59
|
+
# Selenium <= v3.141.0
|
60
|
+
::Selenium::WebDriver::Firefox.driver_path
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
32
65
|
private
|
33
66
|
def headless_chrome_browser_options
|
34
|
-
|
35
|
-
|
36
|
-
options.args << "--disable-gpu" if Gem.win_platform?
|
67
|
+
capabilities.add_argument("--headless")
|
68
|
+
capabilities.add_argument("--disable-gpu") if Gem.win_platform?
|
37
69
|
|
38
|
-
|
70
|
+
capabilities
|
39
71
|
end
|
40
72
|
|
41
73
|
def headless_firefox_browser_options
|
42
|
-
|
43
|
-
options.args << "-headless"
|
74
|
+
capabilities.add_argument("-headless")
|
44
75
|
|
45
|
-
|
76
|
+
capabilities
|
46
77
|
end
|
47
78
|
end
|
48
79
|
end
|
@@ -3,11 +3,14 @@
|
|
3
3
|
module ActionDispatch
|
4
4
|
module SystemTesting
|
5
5
|
class Driver # :nodoc:
|
6
|
-
def initialize(name, **options)
|
6
|
+
def initialize(name, **options, &capabilities)
|
7
7
|
@name = name
|
8
8
|
@browser = Browser.new(options[:using])
|
9
9
|
@screen_size = options[:screen_size]
|
10
10
|
@options = options[:options]
|
11
|
+
@capabilities = capabilities
|
12
|
+
|
13
|
+
@browser.preload unless name == :rack_test
|
11
14
|
end
|
12
15
|
|
13
16
|
def use
|
@@ -22,6 +25,8 @@ module ActionDispatch
|
|
22
25
|
end
|
23
26
|
|
24
27
|
def register
|
28
|
+
define_browser_capabilities(@browser.capabilities)
|
29
|
+
|
25
30
|
Capybara.register_driver @name do |app|
|
26
31
|
case @name
|
27
32
|
when :selenium then register_selenium(app)
|
@@ -31,12 +36,16 @@ module ActionDispatch
|
|
31
36
|
end
|
32
37
|
end
|
33
38
|
|
39
|
+
def define_browser_capabilities(capabilities)
|
40
|
+
@capabilities.call(capabilities) if @capabilities
|
41
|
+
end
|
42
|
+
|
34
43
|
def browser_options
|
35
44
|
@options.merge(options: @browser.options).compact
|
36
45
|
end
|
37
46
|
|
38
47
|
def register_selenium(app)
|
39
|
-
Capybara::Selenium::Driver.new(app, { browser: @browser.type }.merge(browser_options)).tap do |driver|
|
48
|
+
Capybara::Selenium::Driver.new(app, **{ browser: @browser.type }.merge(browser_options)).tap do |driver|
|
40
49
|
driver.browser.manage.window.size = Selenium::WebDriver::Dimension.new(*@screen_size)
|
41
50
|
end
|
42
51
|
end
|
@@ -20,7 +20,7 @@ module ActionDispatch
|
|
20
20
|
# * [+inline+] Display the screenshot in the terminal using the
|
21
21
|
# iTerm image protocol (https://iterm2.com/documentation-images.html).
|
22
22
|
# * [+artifact+] Display the screenshot in the terminal, using the terminal
|
23
|
-
# artifact format (https://buildkite.github.io/terminal/inline-images/).
|
23
|
+
# artifact format (https://buildkite.github.io/terminal-to-html/inline-images/).
|
24
24
|
def take_screenshot
|
25
25
|
save_image
|
26
26
|
puts display_image
|
@@ -39,11 +39,12 @@ module ActionDispatch
|
|
39
39
|
|
40
40
|
private
|
41
41
|
def image_name
|
42
|
-
|
42
|
+
name = method_name[0...225]
|
43
|
+
failed? ? "failures_#{name}" : name
|
43
44
|
end
|
44
45
|
|
45
46
|
def image_path
|
46
|
-
@image_path ||= absolute_image_path.
|
47
|
+
@image_path ||= absolute_image_path.to_s
|
47
48
|
end
|
48
49
|
|
49
50
|
def absolute_image_path
|
@@ -65,7 +66,7 @@ module ActionDispatch
|
|
65
66
|
end
|
66
67
|
|
67
68
|
def display_image
|
68
|
-
message = "[Screenshot]: #{image_path}\n"
|
69
|
+
message = +"[Screenshot]: #{image_path}\n"
|
69
70
|
|
70
71
|
case output_type
|
71
72
|
when "artifact"
|
@@ -80,7 +81,7 @@ module ActionDispatch
|
|
80
81
|
end
|
81
82
|
|
82
83
|
def inline_base64(path)
|
83
|
-
Base64.
|
84
|
+
Base64.strict_encode64(path)
|
84
85
|
end
|
85
86
|
|
86
87
|
def failed?
|
@@ -7,7 +7,6 @@ module ActionDispatch
|
|
7
7
|
DEFAULT_HOST = "http://127.0.0.1"
|
8
8
|
|
9
9
|
def host!(host)
|
10
|
-
super
|
11
10
|
Capybara.app_host = host
|
12
11
|
end
|
13
12
|
|
@@ -16,12 +15,14 @@ module ActionDispatch
|
|
16
15
|
super
|
17
16
|
end
|
18
17
|
|
18
|
+
def before_teardown
|
19
|
+
take_failed_screenshot
|
20
|
+
ensure
|
21
|
+
super
|
22
|
+
end
|
23
|
+
|
19
24
|
def after_teardown
|
20
|
-
|
21
|
-
take_failed_screenshot
|
22
|
-
ensure
|
23
|
-
Capybara.reset_sessions!
|
24
|
-
end
|
25
|
+
Capybara.reset_sessions!
|
25
26
|
ensure
|
26
27
|
super
|
27
28
|
end
|
@@ -79,9 +79,8 @@ module ActionDispatch
|
|
79
79
|
end
|
80
80
|
|
81
81
|
def generate_response_message(expected, actual = @response.response_code)
|
82
|
-
"Expected response to be a <#{code_with_name(expected)}>,"\
|
83
|
-
" but was a <#{code_with_name(actual)}>"
|
84
|
-
.dup.concat(location_if_redirected).concat(response_body_if_short)
|
82
|
+
(+"Expected response to be a <#{code_with_name(expected)}>,"\
|
83
|
+
" but was a <#{code_with_name(actual)}>").concat(location_if_redirected).concat(response_body_if_short)
|
85
84
|
end
|
86
85
|
|
87
86
|
def response_body_if_short
|