actionpack 5.2.1 → 7.0.2.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +264 -220
- data/MIT-LICENSE +1 -1
- data/README.rdoc +6 -6
- data/lib/abstract_controller/asset_paths.rb +1 -1
- data/lib/abstract_controller/base.rb +24 -4
- data/lib/abstract_controller/caching/fragments.rb +8 -24
- data/lib/abstract_controller/caching.rb +2 -2
- data/lib/abstract_controller/callbacks.rb +34 -8
- data/lib/abstract_controller/collector.rb +5 -4
- data/lib/abstract_controller/error.rb +1 -1
- data/lib/abstract_controller/helpers.rb +107 -90
- data/lib/abstract_controller/logger.rb +1 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +19 -1
- data/lib/abstract_controller/rendering.rb +9 -9
- data/lib/abstract_controller/translation.rb +12 -5
- data/lib/abstract_controller/url_for.rb +4 -6
- data/lib/abstract_controller.rb +2 -0
- data/lib/action_controller/api.rb +5 -4
- data/lib/action_controller/base.rb +6 -9
- data/lib/action_controller/caching.rb +1 -3
- data/lib/action_controller/log_subscriber.rb +13 -9
- data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
- data/lib/action_controller/metal/conditional_get.rb +57 -6
- data/lib/action_controller/metal/content_security_policy.rb +2 -3
- data/lib/action_controller/metal/cookies.rb +4 -2
- data/lib/action_controller/metal/data_streaming.rb +9 -18
- data/lib/action_controller/metal/default_headers.rb +17 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +4 -6
- data/lib/action_controller/metal/exceptions.rb +55 -12
- data/lib/action_controller/metal/flash.rb +10 -6
- data/lib/action_controller/metal/head.rb +7 -4
- data/lib/action_controller/metal/helpers.rb +15 -6
- data/lib/action_controller/metal/http_authentication.rb +41 -39
- data/lib/action_controller/metal/implicit_render.rb +5 -15
- data/lib/action_controller/metal/instrumentation.rb +59 -55
- data/lib/action_controller/metal/live.rb +80 -33
- data/lib/action_controller/metal/logging.rb +20 -0
- data/lib/action_controller/metal/mime_responds.rb +22 -7
- data/lib/action_controller/metal/parameter_encoding.rb +35 -4
- data/lib/action_controller/metal/params_wrapper.rb +50 -31
- data/lib/action_controller/metal/permissions_policy.rb +46 -0
- data/lib/action_controller/metal/redirecting.rb +93 -23
- data/lib/action_controller/metal/renderers.rb +4 -4
- data/lib/action_controller/metal/rendering.rb +14 -9
- data/lib/action_controller/metal/request_forgery_protection.rb +160 -58
- data/lib/action_controller/metal/rescue.rb +2 -2
- data/lib/action_controller/metal/streaming.rb +1 -4
- data/lib/action_controller/metal/strong_parameters.rb +236 -88
- data/lib/action_controller/metal/testing.rb +9 -2
- data/lib/action_controller/metal/url_for.rb +1 -1
- data/lib/action_controller/metal.rb +16 -17
- data/lib/action_controller/railtie.rb +49 -6
- data/lib/action_controller/railties/helpers.rb +1 -1
- data/lib/action_controller/renderer.rb +37 -13
- data/lib/action_controller/template_assertions.rb +1 -1
- data/lib/action_controller/test_case.rb +98 -68
- data/lib/action_controller.rb +4 -5
- data/lib/action_dispatch/http/cache.rb +45 -32
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +69 -56
- data/lib/action_dispatch/http/filter_parameters.rb +14 -8
- data/lib/action_dispatch/http/filter_redirect.rb +2 -3
- data/lib/action_dispatch/http/headers.rb +4 -4
- data/lib/action_dispatch/http/mime_negotiation.rb +44 -16
- data/lib/action_dispatch/http/mime_type.rb +47 -30
- data/lib/action_dispatch/http/parameters.rb +18 -27
- data/lib/action_dispatch/http/permissions_policy.rb +173 -0
- data/lib/action_dispatch/http/request.rb +49 -35
- data/lib/action_dispatch/http/response.rb +34 -26
- data/lib/action_dispatch/http/upload.rb +9 -1
- data/lib/action_dispatch/http/url.rb +86 -94
- data/lib/action_dispatch/journey/formatter.rb +55 -31
- data/lib/action_dispatch/journey/gtg/builder.rb +30 -46
- data/lib/action_dispatch/journey/gtg/simulator.rb +15 -8
- data/lib/action_dispatch/journey/gtg/transition_table.rb +78 -21
- data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
- data/lib/action_dispatch/journey/nodes/node.rb +83 -16
- data/lib/action_dispatch/journey/parser.rb +13 -13
- data/lib/action_dispatch/journey/parser.y +1 -1
- data/lib/action_dispatch/journey/path/pattern.rb +42 -34
- data/lib/action_dispatch/journey/route.rb +14 -31
- data/lib/action_dispatch/journey/router/utils.rb +16 -14
- data/lib/action_dispatch/journey/router.rb +27 -35
- data/lib/action_dispatch/journey/routes.rb +3 -5
- data/lib/action_dispatch/journey/scanner.rb +10 -4
- data/lib/action_dispatch/journey/visitors.rb +1 -4
- data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
- data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
- data/lib/action_dispatch/journey.rb +0 -2
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +45 -0
- data/lib/action_dispatch/middleware/callbacks.rb +2 -4
- data/lib/action_dispatch/middleware/cookies.rb +136 -113
- data/lib/action_dispatch/middleware/debug_exceptions.rb +47 -68
- data/lib/action_dispatch/middleware/debug_locks.rb +8 -8
- data/lib/action_dispatch/middleware/debug_view.rb +66 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +79 -30
- data/lib/action_dispatch/middleware/executor.rb +4 -1
- data/lib/action_dispatch/middleware/flash.rb +10 -12
- data/lib/action_dispatch/middleware/host_authorization.rb +159 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +6 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +30 -20
- data/lib/action_dispatch/middleware/request_id.rb +5 -6
- data/lib/action_dispatch/middleware/server_timing.rb +33 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +16 -3
- data/lib/action_dispatch/middleware/session/cache_store.rb +11 -6
- data/lib/action_dispatch/middleware/session/cookie_store.rb +24 -19
- data/lib/action_dispatch/middleware/show_exceptions.rb +20 -11
- data/lib/action_dispatch/middleware/ssl.rb +20 -15
- data/lib/action_dispatch/middleware/stack.rb +79 -7
- data/lib/action_dispatch/middleware/static.rb +150 -94
- 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/_message_and_suggestions.html.erb +22 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +6 -11
- 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 +46 -36
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +25 -6
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +9 -6
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +4 -1
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +121 -15
- 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 +5 -5
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +5 -5
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +16 -2
- data/lib/action_dispatch/railtie.rb +16 -4
- data/lib/action_dispatch/request/session.rb +59 -22
- data/lib/action_dispatch/request/utils.rb +28 -2
- data/lib/action_dispatch/routing/inspector.rb +102 -54
- data/lib/action_dispatch/routing/mapper.rb +184 -156
- data/lib/action_dispatch/routing/polymorphic_routes.rb +21 -19
- data/lib/action_dispatch/routing/redirection.rb +4 -6
- data/lib/action_dispatch/routing/route_set.rb +83 -73
- data/lib/action_dispatch/routing/routes_proxy.rb +1 -1
- data/lib/action_dispatch/routing/url_for.rb +2 -3
- data/lib/action_dispatch/routing.rb +23 -22
- data/lib/action_dispatch/system_test_case.rb +65 -16
- data/lib/action_dispatch/system_testing/browser.rb +43 -16
- data/lib/action_dispatch/system_testing/driver.rb +42 -10
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +58 -12
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +3 -10
- data/lib/action_dispatch/testing/assertion_response.rb +0 -1
- data/lib/action_dispatch/testing/assertions/response.rb +4 -7
- data/lib/action_dispatch/testing/assertions/routing.rb +20 -8
- data/lib/action_dispatch/testing/assertions.rb +3 -6
- data/lib/action_dispatch/testing/integration.rb +61 -30
- data/lib/action_dispatch/testing/request_encoder.rb +2 -2
- data/lib/action_dispatch/testing/test_process.rb +8 -6
- data/lib/action_dispatch/testing/test_request.rb +3 -3
- data/lib/action_dispatch/testing/test_response.rb +4 -32
- data/lib/action_dispatch.rb +15 -7
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack.rb +1 -1
- metadata +44 -25
- data/lib/action_controller/metal/force_ssl.rb +0 -99
- data/lib/action_dispatch/http/parameter_filter.rb +0 -86
- data/lib/action_dispatch/journey/nfa/builder.rb +0 -78
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -49
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -120
- data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +0 -26
@@ -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
|
|
@@ -254,7 +255,7 @@ module ActionDispatch
|
|
254
255
|
autoload :UrlFor
|
255
256
|
autoload :PolymorphicRoutes
|
256
257
|
|
257
|
-
SEPARATORS = %w( / . ? )
|
258
|
-
HTTP_METHODS = [:get, :head, :post, :patch, :put, :delete, :options]
|
258
|
+
SEPARATORS = %w( / . ? ) # :nodoc:
|
259
|
+
HTTP_METHODS = [:get, :head, :post, :patch, :put, :delete, :options] # :nodoc:
|
259
260
|
end
|
260
261
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
gem "capybara", ">=
|
3
|
+
gem "capybara", ">= 3.26"
|
4
4
|
|
5
5
|
require "capybara/dsl"
|
6
6
|
require "capybara/minitest"
|
@@ -10,7 +10,6 @@ require "action_dispatch/system_testing/browser"
|
|
10
10
|
require "action_dispatch/system_testing/server"
|
11
11
|
require "action_dispatch/system_testing/test_helpers/screenshot_helper"
|
12
12
|
require "action_dispatch/system_testing/test_helpers/setup_and_teardown"
|
13
|
-
require "action_dispatch/system_testing/test_helpers/undef_methods"
|
14
13
|
|
15
14
|
module ActionDispatch
|
16
15
|
# = System Testing
|
@@ -27,7 +26,7 @@ module ActionDispatch
|
|
27
26
|
#
|
28
27
|
# Here is an example system test:
|
29
28
|
#
|
30
|
-
# require
|
29
|
+
# require "application_system_test_case"
|
31
30
|
#
|
32
31
|
# class Users::CreateTest < ApplicationSystemTestCase
|
33
32
|
# test "adding a new user" do
|
@@ -73,8 +72,8 @@ module ActionDispatch
|
|
73
72
|
# Headless browsers such as headless Chrome and headless Firefox are also supported.
|
74
73
|
# You can use these browsers by setting the +:using+ argument to +:headless_chrome+ or +:headless_firefox+.
|
75
74
|
#
|
76
|
-
# To use a headless driver, like
|
77
|
-
#
|
75
|
+
# To use a headless driver, like Cuprite, update your Gemfile to use
|
76
|
+
# Cuprite instead of Selenium and then declare the driver name in the
|
78
77
|
# +application_system_test_case.rb+ file. In this case, you would leave out
|
79
78
|
# the +:using+ option because the driver is headless, but you can still use
|
80
79
|
# +:screen_size+ to change the size of the browser screen, also you can use
|
@@ -82,25 +81,45 @@ module ActionDispatch
|
|
82
81
|
# driver documentation to learn about supported options.
|
83
82
|
#
|
84
83
|
# require "test_helper"
|
85
|
-
# require "capybara/
|
84
|
+
# require "capybara/cuprite"
|
86
85
|
#
|
87
86
|
# class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
|
88
|
-
# driven_by :
|
87
|
+
# driven_by :cuprite, screen_size: [1400, 1400], options:
|
89
88
|
# { js_errors: true }
|
90
89
|
# end
|
91
90
|
#
|
91
|
+
# Some drivers require browser capabilities to be passed as a block instead
|
92
|
+
# of through the +options+ hash.
|
93
|
+
#
|
94
|
+
# As an example, if you want to add mobile emulation on chrome, you'll have to
|
95
|
+
# create an instance of selenium's +Chrome::Options+ object and add
|
96
|
+
# capabilities with a block.
|
97
|
+
#
|
98
|
+
# The block will be passed an instance of <tt><Driver>::Options</tt> where you can
|
99
|
+
# define the capabilities you want. Please refer to your driver documentation
|
100
|
+
# to learn about supported options.
|
101
|
+
#
|
102
|
+
# class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
|
103
|
+
# driven_by :selenium, using: :chrome, screen_size: [1024, 768] do |driver_option|
|
104
|
+
# driver_option.add_emulation(device_name: 'iPhone 6')
|
105
|
+
# driver_option.add_extension('path/to/chrome_extension.crx')
|
106
|
+
# end
|
107
|
+
# end
|
108
|
+
#
|
92
109
|
# Because <tt>ActionDispatch::SystemTestCase</tt> is a shim between Capybara
|
93
110
|
# and Rails, any driver that is supported by Capybara is supported by system
|
94
111
|
# tests as long as you include the required gems and files.
|
95
|
-
class SystemTestCase <
|
112
|
+
class SystemTestCase < ActiveSupport::TestCase
|
96
113
|
include Capybara::DSL
|
97
114
|
include Capybara::Minitest::Assertions
|
98
115
|
include SystemTesting::TestHelpers::SetupAndTeardown
|
99
116
|
include SystemTesting::TestHelpers::ScreenshotHelper
|
100
|
-
|
117
|
+
|
118
|
+
DEFAULT_HOST = "http://127.0.0.1"
|
101
119
|
|
102
120
|
def initialize(*) # :nodoc:
|
103
121
|
super
|
122
|
+
self.class.driven_by(:selenium) unless self.class.driver?
|
104
123
|
self.class.driver.use
|
105
124
|
end
|
106
125
|
|
@@ -123,7 +142,7 @@ module ActionDispatch
|
|
123
142
|
#
|
124
143
|
# Examples:
|
125
144
|
#
|
126
|
-
# driven_by :
|
145
|
+
# driven_by :cuprite
|
127
146
|
#
|
128
147
|
# driven_by :selenium, screen_size: [800, 800]
|
129
148
|
#
|
@@ -134,14 +153,44 @@ module ActionDispatch
|
|
134
153
|
# driven_by :selenium, using: :firefox
|
135
154
|
#
|
136
155
|
# driven_by :selenium, using: :headless_firefox
|
137
|
-
def self.driven_by(driver, using: :chrome, screen_size: [1400, 1400], options: {})
|
138
|
-
|
156
|
+
def self.driven_by(driver, using: :chrome, screen_size: [1400, 1400], options: {}, &capabilities)
|
157
|
+
driver_options = { using: using, screen_size: screen_size, options: options }
|
158
|
+
|
159
|
+
self.driver = SystemTesting::Driver.new(driver, **driver_options, &capabilities)
|
139
160
|
end
|
140
161
|
|
141
|
-
|
162
|
+
private
|
163
|
+
def url_helpers
|
164
|
+
@url_helpers ||=
|
165
|
+
if ActionDispatch.test_app
|
166
|
+
Class.new do
|
167
|
+
include ActionDispatch.test_app.routes.url_helpers
|
168
|
+
include ActionDispatch.test_app.routes.mounted_helpers
|
142
169
|
|
143
|
-
|
144
|
-
|
170
|
+
def url_options
|
171
|
+
default_url_options.reverse_merge(host: app_host)
|
172
|
+
end
|
173
|
+
|
174
|
+
def app_host
|
175
|
+
Capybara.app_host || Capybara.current_session.server_url || DEFAULT_HOST
|
176
|
+
end
|
177
|
+
end.new
|
178
|
+
end
|
179
|
+
end
|
145
180
|
|
146
|
-
|
181
|
+
def method_missing(name, *args, &block)
|
182
|
+
if url_helpers.respond_to?(name)
|
183
|
+
url_helpers.public_send(name, *args, &block)
|
184
|
+
else
|
185
|
+
super
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def respond_to_missing?(name, include_private = false)
|
190
|
+
url_helpers.respond_to?(name)
|
191
|
+
end
|
192
|
+
end
|
147
193
|
end
|
194
|
+
|
195
|
+
ActiveSupport.run_load_hooks :action_dispatch_system_test_case, ActionDispatch::SystemTestCase
|
196
|
+
ActionDispatch::SystemTestCase.start_application
|
@@ -3,10 +3,11 @@
|
|
3
3
|
module ActionDispatch
|
4
4
|
module SystemTesting
|
5
5
|
class Browser # :nodoc:
|
6
|
-
attr_reader :name
|
6
|
+
attr_reader :name, :options
|
7
7
|
|
8
8
|
def initialize(name)
|
9
9
|
@name = name
|
10
|
+
set_default_options
|
10
11
|
end
|
11
12
|
|
12
13
|
def type
|
@@ -20,29 +21,55 @@ module ActionDispatch
|
|
20
21
|
end
|
21
22
|
end
|
22
23
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
24
|
+
def configure
|
25
|
+
initialize_options
|
26
|
+
yield options if block_given? && options
|
27
|
+
end
|
28
|
+
|
29
|
+
# driver_path can be configured as a proc. The webdrivers gem uses this
|
30
|
+
# proc to update web drivers. Running this proc early allows us to only
|
31
|
+
# update the webdriver once and avoid race conditions when using
|
32
|
+
# parallel tests.
|
33
|
+
def preload
|
34
|
+
case type
|
35
|
+
when :chrome
|
36
|
+
::Selenium::WebDriver::Chrome::Service.driver_path&.call
|
37
|
+
when :firefox
|
38
|
+
::Selenium::WebDriver::Firefox::Service.driver_path&.call
|
29
39
|
end
|
30
40
|
end
|
31
41
|
|
32
42
|
private
|
33
|
-
def
|
34
|
-
options
|
35
|
-
|
36
|
-
|
43
|
+
def initialize_options
|
44
|
+
@options ||=
|
45
|
+
case type
|
46
|
+
when :chrome
|
47
|
+
::Selenium::WebDriver::Chrome::Options.new
|
48
|
+
when :firefox
|
49
|
+
::Selenium::WebDriver::Firefox::Options.new
|
50
|
+
end
|
51
|
+
end
|
37
52
|
|
38
|
-
|
53
|
+
def set_default_options
|
54
|
+
case name
|
55
|
+
when :headless_chrome
|
56
|
+
set_headless_chrome_browser_options
|
57
|
+
when :headless_firefox
|
58
|
+
set_headless_firefox_browser_options
|
59
|
+
end
|
39
60
|
end
|
40
61
|
|
41
|
-
def
|
42
|
-
|
43
|
-
|
62
|
+
def set_headless_chrome_browser_options
|
63
|
+
configure do |capabilities|
|
64
|
+
capabilities.add_argument("--headless")
|
65
|
+
capabilities.add_argument("--disable-gpu") if Gem.win_platform?
|
66
|
+
end
|
67
|
+
end
|
44
68
|
|
45
|
-
|
69
|
+
def set_headless_firefox_browser_options
|
70
|
+
configure do |capabilities|
|
71
|
+
capabilities.add_argument("-headless")
|
72
|
+
end
|
46
73
|
end
|
47
74
|
end
|
48
75
|
end
|
@@ -3,11 +3,31 @@
|
|
3
3
|
module ActionDispatch
|
4
4
|
module SystemTesting
|
5
5
|
class Driver # :nodoc:
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
attr_reader :name
|
7
|
+
|
8
|
+
def initialize(driver_type, **options, &capabilities)
|
9
|
+
@driver_type = driver_type
|
9
10
|
@screen_size = options[:screen_size]
|
10
|
-
@options = options[:options]
|
11
|
+
@options = options[:options] || {}
|
12
|
+
@name = @options.delete(:name) || driver_type
|
13
|
+
@capabilities = capabilities
|
14
|
+
|
15
|
+
if [:poltergeist, :webkit].include?(driver_type)
|
16
|
+
ActiveSupport::Deprecation.warn <<~MSG.squish
|
17
|
+
Poltergeist and capybara-webkit are not maintained already.
|
18
|
+
Driver registration of :poltergeist or :webkit is deprecated and will be removed in Rails 7.1.
|
19
|
+
You can still use :selenium, and also :cuprite is available for alternative to Poltergeist.
|
20
|
+
MSG
|
21
|
+
end
|
22
|
+
|
23
|
+
if driver_type == :selenium
|
24
|
+
gem "selenium-webdriver", ">= 4.0.0"
|
25
|
+
require "selenium/webdriver"
|
26
|
+
@browser = Browser.new(options[:using])
|
27
|
+
@browser.preload
|
28
|
+
else
|
29
|
+
@browser = nil
|
30
|
+
end
|
11
31
|
end
|
12
32
|
|
13
33
|
def use
|
@@ -18,25 +38,29 @@ module ActionDispatch
|
|
18
38
|
|
19
39
|
private
|
20
40
|
def registerable?
|
21
|
-
[:selenium, :poltergeist, :webkit].include?(@
|
41
|
+
[:selenium, :poltergeist, :webkit, :cuprite, :rack_test].include?(@driver_type)
|
22
42
|
end
|
23
43
|
|
24
44
|
def register
|
25
|
-
|
26
|
-
|
45
|
+
@browser&.configure(&@capabilities)
|
46
|
+
|
47
|
+
Capybara.register_driver name do |app|
|
48
|
+
case @driver_type
|
27
49
|
when :selenium then register_selenium(app)
|
28
50
|
when :poltergeist then register_poltergeist(app)
|
29
51
|
when :webkit then register_webkit(app)
|
52
|
+
when :cuprite then register_cuprite(app)
|
53
|
+
when :rack_test then register_rack_test(app)
|
30
54
|
end
|
31
55
|
end
|
32
56
|
end
|
33
57
|
|
34
58
|
def browser_options
|
35
|
-
@options.merge(
|
59
|
+
@options.merge(capabilities: @browser.options).compact
|
36
60
|
end
|
37
61
|
|
38
62
|
def register_selenium(app)
|
39
|
-
Capybara::Selenium::Driver.new(app,
|
63
|
+
Capybara::Selenium::Driver.new(app, browser: @browser.type, **browser_options).tap do |driver|
|
40
64
|
driver.browser.manage.window.size = Selenium::WebDriver::Dimension.new(*@screen_size)
|
41
65
|
end
|
42
66
|
end
|
@@ -51,8 +75,16 @@ module ActionDispatch
|
|
51
75
|
end
|
52
76
|
end
|
53
77
|
|
78
|
+
def register_cuprite(app)
|
79
|
+
Capybara::Cuprite::Driver.new(app, @options.merge(window_size: @screen_size))
|
80
|
+
end
|
81
|
+
|
82
|
+
def register_rack_test(app)
|
83
|
+
Capybara::RackTest::Driver.new(app, respect_data_method: true, **@options)
|
84
|
+
end
|
85
|
+
|
54
86
|
def setup
|
55
|
-
Capybara.current_driver =
|
87
|
+
Capybara.current_driver = name
|
56
88
|
end
|
57
89
|
end
|
58
90
|
end
|
@@ -9,10 +9,19 @@ module ActionDispatch
|
|
9
9
|
#
|
10
10
|
# +take_screenshot+ can be used at any point in your system tests to take
|
11
11
|
# a screenshot of the current state. This can be useful for debugging or
|
12
|
-
# automating visual testing.
|
12
|
+
# automating visual testing. You can take multiple screenshots per test
|
13
|
+
# to investigate changes at different points during your test. These will be
|
14
|
+
# named with a sequential prefix (or 'failed' for failing tests)
|
13
15
|
#
|
14
16
|
# The screenshot will be displayed in your console, if supported.
|
15
17
|
#
|
18
|
+
# The default screenshots directory is +tmp/screenshots+ but you can set a different
|
19
|
+
# one with +Capybara.save_path+
|
20
|
+
#
|
21
|
+
# You can set the +RAILS_SYSTEM_TESTING_SCREENSHOT_HTML+ environment variable to
|
22
|
+
# save the HTML from the page that is being screenshotted so you can investigate the
|
23
|
+
# elements on the page at the time of the screenshot
|
24
|
+
#
|
16
25
|
# You can set the +RAILS_SYSTEM_TESTING_SCREENSHOT+ environment variable to
|
17
26
|
# control the output. Possible values are:
|
18
27
|
# * [+simple+ (default)] Only displays the screenshot path.
|
@@ -20,8 +29,10 @@ module ActionDispatch
|
|
20
29
|
# * [+inline+] Display the screenshot in the terminal using the
|
21
30
|
# iTerm image protocol (https://iterm2.com/documentation-images.html).
|
22
31
|
# * [+artifact+] Display the screenshot in the terminal, using the terminal
|
23
|
-
# artifact format (https://buildkite.github.io/terminal/inline-images/).
|
32
|
+
# artifact format (https://buildkite.github.io/terminal-to-html/inline-images/).
|
24
33
|
def take_screenshot
|
34
|
+
increment_unique
|
35
|
+
save_html if save_html?
|
25
36
|
save_image
|
26
37
|
puts display_image
|
27
38
|
end
|
@@ -29,25 +40,59 @@ module ActionDispatch
|
|
29
40
|
# Takes a screenshot of the current page in the browser if the test
|
30
41
|
# failed.
|
31
42
|
#
|
32
|
-
# +take_failed_screenshot+ is
|
33
|
-
# that is generated with the application. To take screenshots when a test
|
34
|
-
# fails add +take_failed_screenshot+ to the teardown block before clearing
|
35
|
-
# sessions.
|
43
|
+
# +take_failed_screenshot+ is called during system test teardown.
|
36
44
|
def take_failed_screenshot
|
37
|
-
take_screenshot if failed? && supports_screenshot?
|
45
|
+
take_screenshot if failed? && supports_screenshot? && Capybara::Session.instance_created?
|
38
46
|
end
|
39
47
|
|
40
48
|
private
|
49
|
+
attr_accessor :_screenshot_counter
|
50
|
+
|
51
|
+
def save_html?
|
52
|
+
ENV["RAILS_SYSTEM_TESTING_SCREENSHOT_HTML"] == "1"
|
53
|
+
end
|
54
|
+
|
55
|
+
def increment_unique
|
56
|
+
@_screenshot_counter ||= 0
|
57
|
+
@_screenshot_counter += 1
|
58
|
+
end
|
59
|
+
|
60
|
+
def unique
|
61
|
+
failed? ? "failures" : (_screenshot_counter || 0).to_s
|
62
|
+
end
|
63
|
+
|
41
64
|
def image_name
|
42
|
-
|
65
|
+
sanitized_method_name = method_name.tr("/\\", "--")
|
66
|
+
name = "#{unique}_#{sanitized_method_name}"
|
67
|
+
name[0...225]
|
43
68
|
end
|
44
69
|
|
45
70
|
def image_path
|
46
|
-
|
71
|
+
absolute_image_path.to_s
|
72
|
+
end
|
73
|
+
|
74
|
+
def html_path
|
75
|
+
absolute_html_path.to_s
|
76
|
+
end
|
77
|
+
|
78
|
+
def absolute_path
|
79
|
+
Rails.root.join(screenshots_dir, image_name)
|
80
|
+
end
|
81
|
+
|
82
|
+
def screenshots_dir
|
83
|
+
Capybara.save_path.presence || "tmp/screenshots"
|
47
84
|
end
|
48
85
|
|
49
86
|
def absolute_image_path
|
50
|
-
|
87
|
+
"#{absolute_path}.png"
|
88
|
+
end
|
89
|
+
|
90
|
+
def absolute_html_path
|
91
|
+
"#{absolute_path}.html"
|
92
|
+
end
|
93
|
+
|
94
|
+
def save_html
|
95
|
+
page.save_page(absolute_html_path)
|
51
96
|
end
|
52
97
|
|
53
98
|
def save_image
|
@@ -65,7 +110,8 @@ module ActionDispatch
|
|
65
110
|
end
|
66
111
|
|
67
112
|
def display_image
|
68
|
-
message = "[Screenshot]: #{image_path}\n"
|
113
|
+
message = +"[Screenshot Image]: #{image_path}\n"
|
114
|
+
message << +"[Screenshot HTML]: #{html_path}\n" if save_html?
|
69
115
|
|
70
116
|
case output_type
|
71
117
|
when "artifact"
|
@@ -80,7 +126,7 @@ module ActionDispatch
|
|
80
126
|
end
|
81
127
|
|
82
128
|
def inline_base64(path)
|
83
|
-
Base64.
|
129
|
+
Base64.strict_encode64(path)
|
84
130
|
end
|
85
131
|
|
86
132
|
def failed?
|
@@ -4,20 +4,13 @@ module ActionDispatch
|
|
4
4
|
module SystemTesting
|
5
5
|
module TestHelpers
|
6
6
|
module SetupAndTeardown # :nodoc:
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
super
|
11
|
-
Capybara.app_host = host
|
12
|
-
end
|
13
|
-
|
14
|
-
def before_setup
|
15
|
-
host! DEFAULT_HOST
|
7
|
+
def before_teardown
|
8
|
+
take_failed_screenshot
|
9
|
+
ensure
|
16
10
|
super
|
17
11
|
end
|
18
12
|
|
19
13
|
def after_teardown
|
20
|
-
take_failed_screenshot
|
21
14
|
Capybara.reset_sessions!
|
22
15
|
ensure
|
23
16
|
super
|
@@ -31,15 +31,13 @@ module ActionDispatch
|
|
31
31
|
message ||= generate_response_message(type)
|
32
32
|
|
33
33
|
if RESPONSE_PREDICATES.keys.include?(type)
|
34
|
-
assert @response.
|
34
|
+
assert @response.public_send(RESPONSE_PREDICATES[type]), message
|
35
35
|
else
|
36
36
|
assert_equal AssertionResponse.new(type).code, @response.response_code, message
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
# Asserts that the
|
41
|
-
# This match can be partial, such that <tt>assert_redirected_to(controller: "weblog")</tt> will also
|
42
|
-
# match the redirection of <tt>redirect_to(controller: "weblog", action: "show")</tt> and so on.
|
40
|
+
# Asserts that the response is a redirect to a URL matching the given options.
|
43
41
|
#
|
44
42
|
# # Asserts that the redirection was to the "index" action on the WeblogController
|
45
43
|
# assert_redirected_to controller: "weblog", action: "index"
|
@@ -79,9 +77,8 @@ module ActionDispatch
|
|
79
77
|
end
|
80
78
|
|
81
79
|
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)
|
80
|
+
(+"Expected response to be a <#{code_with_name(expected)}>,"\
|
81
|
+
" but was a <#{code_with_name(actual)}>").concat(location_if_redirected).concat(response_body_if_short)
|
85
82
|
end
|
86
83
|
|
87
84
|
def response_body_if_short
|