omg-actionpack 8.0.0.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +129 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +57 -0
- data/lib/abstract_controller/asset_paths.rb +14 -0
- data/lib/abstract_controller/base.rb +299 -0
- data/lib/abstract_controller/caching/fragments.rb +149 -0
- data/lib/abstract_controller/caching.rb +68 -0
- data/lib/abstract_controller/callbacks.rb +265 -0
- data/lib/abstract_controller/collector.rb +44 -0
- data/lib/abstract_controller/deprecator.rb +9 -0
- data/lib/abstract_controller/error.rb +8 -0
- data/lib/abstract_controller/helpers.rb +243 -0
- data/lib/abstract_controller/logger.rb +16 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +25 -0
- data/lib/abstract_controller/rendering.rb +126 -0
- data/lib/abstract_controller/translation.rb +42 -0
- data/lib/abstract_controller/url_for.rb +37 -0
- data/lib/abstract_controller.rb +36 -0
- data/lib/action_controller/api/api_rendering.rb +18 -0
- data/lib/action_controller/api.rb +155 -0
- data/lib/action_controller/base.rb +332 -0
- data/lib/action_controller/caching.rb +49 -0
- data/lib/action_controller/deprecator.rb +9 -0
- data/lib/action_controller/form_builder.rb +55 -0
- data/lib/action_controller/log_subscriber.rb +96 -0
- data/lib/action_controller/metal/allow_browser.rb +123 -0
- data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
- data/lib/action_controller/metal/conditional_get.rb +341 -0
- data/lib/action_controller/metal/content_security_policy.rb +86 -0
- data/lib/action_controller/metal/cookies.rb +20 -0
- data/lib/action_controller/metal/data_streaming.rb +154 -0
- data/lib/action_controller/metal/default_headers.rb +21 -0
- data/lib/action_controller/metal/etag_with_flash.rb +22 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +59 -0
- data/lib/action_controller/metal/exceptions.rb +106 -0
- data/lib/action_controller/metal/flash.rb +67 -0
- data/lib/action_controller/metal/head.rb +67 -0
- data/lib/action_controller/metal/helpers.rb +129 -0
- data/lib/action_controller/metal/http_authentication.rb +565 -0
- data/lib/action_controller/metal/implicit_render.rb +67 -0
- data/lib/action_controller/metal/instrumentation.rb +120 -0
- data/lib/action_controller/metal/live.rb +398 -0
- data/lib/action_controller/metal/logging.rb +22 -0
- data/lib/action_controller/metal/mime_responds.rb +337 -0
- data/lib/action_controller/metal/parameter_encoding.rb +84 -0
- data/lib/action_controller/metal/params_wrapper.rb +312 -0
- data/lib/action_controller/metal/permissions_policy.rb +38 -0
- data/lib/action_controller/metal/rate_limiting.rb +62 -0
- data/lib/action_controller/metal/redirecting.rb +251 -0
- data/lib/action_controller/metal/renderers.rb +181 -0
- data/lib/action_controller/metal/rendering.rb +260 -0
- data/lib/action_controller/metal/request_forgery_protection.rb +667 -0
- data/lib/action_controller/metal/rescue.rb +33 -0
- data/lib/action_controller/metal/streaming.rb +183 -0
- data/lib/action_controller/metal/strong_parameters.rb +1546 -0
- data/lib/action_controller/metal/testing.rb +25 -0
- data/lib/action_controller/metal/url_for.rb +65 -0
- data/lib/action_controller/metal.rb +339 -0
- data/lib/action_controller/railtie.rb +149 -0
- data/lib/action_controller/railties/helpers.rb +26 -0
- data/lib/action_controller/renderer.rb +161 -0
- data/lib/action_controller/template_assertions.rb +13 -0
- data/lib/action_controller/test_case.rb +691 -0
- data/lib/action_controller.rb +80 -0
- data/lib/action_dispatch/constants.rb +34 -0
- data/lib/action_dispatch/deprecator.rb +9 -0
- data/lib/action_dispatch/http/cache.rb +249 -0
- data/lib/action_dispatch/http/content_disposition.rb +47 -0
- data/lib/action_dispatch/http/content_security_policy.rb +365 -0
- data/lib/action_dispatch/http/filter_parameters.rb +80 -0
- data/lib/action_dispatch/http/filter_redirect.rb +50 -0
- data/lib/action_dispatch/http/headers.rb +134 -0
- data/lib/action_dispatch/http/mime_negotiation.rb +187 -0
- data/lib/action_dispatch/http/mime_type.rb +389 -0
- data/lib/action_dispatch/http/mime_types.rb +54 -0
- data/lib/action_dispatch/http/parameters.rb +119 -0
- data/lib/action_dispatch/http/permissions_policy.rb +189 -0
- data/lib/action_dispatch/http/rack_cache.rb +67 -0
- data/lib/action_dispatch/http/request.rb +498 -0
- data/lib/action_dispatch/http/response.rb +556 -0
- data/lib/action_dispatch/http/upload.rb +107 -0
- data/lib/action_dispatch/http/url.rb +344 -0
- data/lib/action_dispatch/journey/formatter.rb +226 -0
- data/lib/action_dispatch/journey/gtg/builder.rb +149 -0
- data/lib/action_dispatch/journey/gtg/simulator.rb +50 -0
- data/lib/action_dispatch/journey/gtg/transition_table.rb +217 -0
- data/lib/action_dispatch/journey/nfa/dot.rb +27 -0
- data/lib/action_dispatch/journey/nodes/node.rb +208 -0
- data/lib/action_dispatch/journey/parser.rb +103 -0
- data/lib/action_dispatch/journey/path/pattern.rb +209 -0
- data/lib/action_dispatch/journey/route.rb +189 -0
- data/lib/action_dispatch/journey/router/utils.rb +105 -0
- data/lib/action_dispatch/journey/router.rb +151 -0
- data/lib/action_dispatch/journey/routes.rb +82 -0
- data/lib/action_dispatch/journey/scanner.rb +70 -0
- data/lib/action_dispatch/journey/visitors.rb +267 -0
- data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
- data/lib/action_dispatch/journey/visualizer/fsm.js +159 -0
- data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
- data/lib/action_dispatch/journey.rb +7 -0
- data/lib/action_dispatch/log_subscriber.rb +25 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
- data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
- data/lib/action_dispatch/middleware/callbacks.rb +38 -0
- data/lib/action_dispatch/middleware/cookies.rb +719 -0
- data/lib/action_dispatch/middleware/debug_exceptions.rb +206 -0
- data/lib/action_dispatch/middleware/debug_locks.rb +129 -0
- data/lib/action_dispatch/middleware/debug_view.rb +73 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +350 -0
- data/lib/action_dispatch/middleware/executor.rb +32 -0
- data/lib/action_dispatch/middleware/flash.rb +318 -0
- data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +64 -0
- data/lib/action_dispatch/middleware/reloader.rb +16 -0
- data/lib/action_dispatch/middleware/remote_ip.rb +199 -0
- data/lib/action_dispatch/middleware/request_id.rb +50 -0
- data/lib/action_dispatch/middleware/server_timing.rb +78 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +112 -0
- data/lib/action_dispatch/middleware/session/cache_store.rb +66 -0
- data/lib/action_dispatch/middleware/session/cookie_store.rb +129 -0
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +34 -0
- data/lib/action_dispatch/middleware/show_exceptions.rb +88 -0
- data/lib/action_dispatch/middleware/ssl.rb +180 -0
- data/lib/action_dispatch/middleware/stack.rb +194 -0
- data/lib/action_dispatch/middleware/static.rb +192 -0
- 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 +17 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +36 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +62 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +12 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +35 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +16 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +284 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +23 -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 +11 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +19 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +232 -0
- data/lib/action_dispatch/railtie.rb +77 -0
- data/lib/action_dispatch/request/session.rb +283 -0
- data/lib/action_dispatch/request/utils.rb +109 -0
- data/lib/action_dispatch/routing/endpoint.rb +19 -0
- data/lib/action_dispatch/routing/inspector.rb +323 -0
- data/lib/action_dispatch/routing/mapper.rb +2372 -0
- data/lib/action_dispatch/routing/polymorphic_routes.rb +363 -0
- data/lib/action_dispatch/routing/redirection.rb +218 -0
- data/lib/action_dispatch/routing/route_set.rb +958 -0
- data/lib/action_dispatch/routing/routes_proxy.rb +66 -0
- data/lib/action_dispatch/routing/url_for.rb +244 -0
- data/lib/action_dispatch/routing.rb +262 -0
- data/lib/action_dispatch/system_test_case.rb +206 -0
- data/lib/action_dispatch/system_testing/browser.rb +75 -0
- data/lib/action_dispatch/system_testing/driver.rb +85 -0
- data/lib/action_dispatch/system_testing/server.rb +33 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +164 -0
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +23 -0
- data/lib/action_dispatch/testing/assertion_response.rb +48 -0
- data/lib/action_dispatch/testing/assertions/response.rb +114 -0
- data/lib/action_dispatch/testing/assertions/routing.rb +343 -0
- data/lib/action_dispatch/testing/assertions.rb +25 -0
- data/lib/action_dispatch/testing/integration.rb +694 -0
- data/lib/action_dispatch/testing/request_encoder.rb +60 -0
- data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
- data/lib/action_dispatch/testing/test_process.rb +57 -0
- data/lib/action_dispatch/testing/test_request.rb +73 -0
- data/lib/action_dispatch/testing/test_response.rb +58 -0
- data/lib/action_dispatch.rb +147 -0
- data/lib/action_pack/gem_version.rb +19 -0
- data/lib/action_pack/version.rb +12 -0
- data/lib/action_pack.rb +27 -0
- metadata +375 -0
@@ -0,0 +1,206 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
gem "capybara", ">= 3.26"
|
6
|
+
|
7
|
+
require "capybara/dsl"
|
8
|
+
require "capybara/minitest"
|
9
|
+
require "action_controller"
|
10
|
+
require "action_dispatch/system_testing/driver"
|
11
|
+
require "action_dispatch/system_testing/browser"
|
12
|
+
require "action_dispatch/system_testing/server"
|
13
|
+
require "action_dispatch/system_testing/test_helpers/screenshot_helper"
|
14
|
+
require "action_dispatch/system_testing/test_helpers/setup_and_teardown"
|
15
|
+
|
16
|
+
module ActionDispatch
|
17
|
+
# # System Testing
|
18
|
+
#
|
19
|
+
# System tests let you test applications in the browser. Because system tests
|
20
|
+
# use a real browser experience, you can test all of your JavaScript easily from
|
21
|
+
# your test suite.
|
22
|
+
#
|
23
|
+
# To create a system test in your application, extend your test class from
|
24
|
+
# `ApplicationSystemTestCase`. System tests use Capybara as a base and allow you
|
25
|
+
# to configure the settings through your `application_system_test_case.rb` file
|
26
|
+
# that is generated with a new application or scaffold.
|
27
|
+
#
|
28
|
+
# Here is an example system test:
|
29
|
+
#
|
30
|
+
# require "application_system_test_case"
|
31
|
+
#
|
32
|
+
# class Users::CreateTest < ApplicationSystemTestCase
|
33
|
+
# test "adding a new user" do
|
34
|
+
# visit users_path
|
35
|
+
# click_on 'New User'
|
36
|
+
#
|
37
|
+
# fill_in 'Name', with: 'Arya'
|
38
|
+
# click_on 'Create User'
|
39
|
+
#
|
40
|
+
# assert_text 'Arya'
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# When generating an application or scaffold, an
|
45
|
+
# `application_system_test_case.rb` file will also be generated containing the
|
46
|
+
# base class for system testing. This is where you can change the driver, add
|
47
|
+
# Capybara settings, and other configuration for your system tests.
|
48
|
+
#
|
49
|
+
# require "test_helper"
|
50
|
+
#
|
51
|
+
# class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
|
52
|
+
# driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# By default, `ActionDispatch::SystemTestCase` is driven by the Selenium driver,
|
56
|
+
# with the Chrome browser, and a browser size of 1400x1400.
|
57
|
+
#
|
58
|
+
# Changing the driver configuration options is easy. Let's say you want to use
|
59
|
+
# the Firefox browser instead of Chrome. In your
|
60
|
+
# `application_system_test_case.rb` file add the following:
|
61
|
+
#
|
62
|
+
# require "test_helper"
|
63
|
+
#
|
64
|
+
# class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
|
65
|
+
# driven_by :selenium, using: :firefox
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# `driven_by` has a required argument for the driver name. The keyword arguments
|
69
|
+
# are `:using` for the browser and `:screen_size` to change the size of the
|
70
|
+
# browser screen. These two options are not applicable for headless drivers and
|
71
|
+
# will be silently ignored if passed.
|
72
|
+
#
|
73
|
+
# Headless browsers such as headless Chrome and headless Firefox are also
|
74
|
+
# supported. You can use these browsers by setting the `:using` argument to
|
75
|
+
# `:headless_chrome` or `:headless_firefox`.
|
76
|
+
#
|
77
|
+
# To use a headless driver, like Cuprite, update your Gemfile to use Cuprite
|
78
|
+
# instead of Selenium and then declare the driver name in the
|
79
|
+
# `application_system_test_case.rb` file. In this case, you would leave out the
|
80
|
+
# `:using` option because the driver is headless, but you can still use
|
81
|
+
# `:screen_size` to change the size of the browser screen, also you can use
|
82
|
+
# `:options` to pass options supported by the driver. Please refer to your
|
83
|
+
# driver documentation to learn about supported options.
|
84
|
+
#
|
85
|
+
# require "test_helper"
|
86
|
+
# require "capybara/cuprite"
|
87
|
+
#
|
88
|
+
# class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
|
89
|
+
# driven_by :cuprite, screen_size: [1400, 1400], options:
|
90
|
+
# { js_errors: true }
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# Some drivers require browser capabilities to be passed as a block instead of
|
94
|
+
# through the `options` hash.
|
95
|
+
#
|
96
|
+
# As an example, if you want to add mobile emulation on chrome, you'll have to
|
97
|
+
# create an instance of selenium's `Chrome::Options` object and add capabilities
|
98
|
+
# with a block.
|
99
|
+
#
|
100
|
+
# The block will be passed an instance of `<Driver>::Options` where you can
|
101
|
+
# define the capabilities you want. Please refer to your driver documentation to
|
102
|
+
# learn about supported options.
|
103
|
+
#
|
104
|
+
# class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
|
105
|
+
# driven_by :selenium, using: :chrome, screen_size: [1024, 768] do |driver_option|
|
106
|
+
# driver_option.add_emulation(device_name: 'iPhone 6')
|
107
|
+
# driver_option.add_extension('path/to/chrome_extension.crx')
|
108
|
+
# end
|
109
|
+
# end
|
110
|
+
#
|
111
|
+
# Because `ActionDispatch::SystemTestCase` is a shim between Capybara and Rails,
|
112
|
+
# any driver that is supported by Capybara is supported by system tests as long
|
113
|
+
# as you include the required gems and files.
|
114
|
+
class SystemTestCase < ActiveSupport::TestCase
|
115
|
+
include Capybara::DSL
|
116
|
+
include Capybara::Minitest::Assertions
|
117
|
+
include SystemTesting::TestHelpers::SetupAndTeardown
|
118
|
+
include SystemTesting::TestHelpers::ScreenshotHelper
|
119
|
+
|
120
|
+
DEFAULT_HOST = "http://127.0.0.1"
|
121
|
+
|
122
|
+
def initialize(*) # :nodoc:
|
123
|
+
super
|
124
|
+
self.class.driven_by(:selenium) unless self.class.driver?
|
125
|
+
self.class.driver.use
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.start_application # :nodoc:
|
129
|
+
Capybara.app = Rack::Builder.new do
|
130
|
+
map "/" do
|
131
|
+
run Rails.application
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
SystemTesting::Server.new.run
|
136
|
+
end
|
137
|
+
|
138
|
+
class_attribute :driver, instance_accessor: false
|
139
|
+
|
140
|
+
# System Test configuration options
|
141
|
+
#
|
142
|
+
# The default settings are Selenium, using Chrome, with a screen size of
|
143
|
+
# 1400x1400.
|
144
|
+
#
|
145
|
+
# Examples:
|
146
|
+
#
|
147
|
+
# driven_by :cuprite
|
148
|
+
#
|
149
|
+
# driven_by :selenium, screen_size: [800, 800]
|
150
|
+
#
|
151
|
+
# driven_by :selenium, using: :chrome
|
152
|
+
#
|
153
|
+
# driven_by :selenium, using: :headless_chrome
|
154
|
+
#
|
155
|
+
# driven_by :selenium, using: :firefox
|
156
|
+
#
|
157
|
+
# driven_by :selenium, using: :headless_firefox
|
158
|
+
def self.driven_by(driver, using: :chrome, screen_size: [1400, 1400], options: {}, &capabilities)
|
159
|
+
driver_options = { using: using, screen_size: screen_size, options: options }
|
160
|
+
|
161
|
+
self.driver = SystemTesting::Driver.new(driver, **driver_options, &capabilities)
|
162
|
+
end
|
163
|
+
|
164
|
+
# Configuration for the System Test application server.
|
165
|
+
#
|
166
|
+
# By default this is localhost. This method allows the host and port to be specified manually.
|
167
|
+
def self.served_by(host:, port:)
|
168
|
+
Capybara.server_host = host
|
169
|
+
Capybara.server_port = port
|
170
|
+
end
|
171
|
+
|
172
|
+
private
|
173
|
+
def url_helpers
|
174
|
+
@url_helpers ||=
|
175
|
+
if ActionDispatch.test_app
|
176
|
+
Class.new do
|
177
|
+
include ActionDispatch.test_app.routes.url_helpers
|
178
|
+
include ActionDispatch.test_app.routes.mounted_helpers
|
179
|
+
|
180
|
+
def url_options
|
181
|
+
default_url_options.reverse_merge(host: app_host)
|
182
|
+
end
|
183
|
+
|
184
|
+
def app_host
|
185
|
+
Capybara.app_host || Capybara.current_session.server_url || DEFAULT_HOST
|
186
|
+
end
|
187
|
+
end.new
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def method_missing(name, ...)
|
192
|
+
if url_helpers.respond_to?(name)
|
193
|
+
url_helpers.public_send(name, ...)
|
194
|
+
else
|
195
|
+
super
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def respond_to_missing?(name, include_private = false)
|
200
|
+
url_helpers.respond_to?(name)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
ActiveSupport.run_load_hooks :action_dispatch_system_test_case, ActionDispatch::SystemTestCase
|
206
|
+
ActionDispatch::SystemTestCase.start_application
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
module ActionDispatch
|
6
|
+
module SystemTesting
|
7
|
+
class Browser # :nodoc:
|
8
|
+
attr_reader :name
|
9
|
+
|
10
|
+
def initialize(name)
|
11
|
+
@name = name
|
12
|
+
end
|
13
|
+
|
14
|
+
def type
|
15
|
+
case name
|
16
|
+
when :headless_chrome
|
17
|
+
:chrome
|
18
|
+
when :headless_firefox
|
19
|
+
:firefox
|
20
|
+
else
|
21
|
+
name
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def options
|
26
|
+
@options ||=
|
27
|
+
case type
|
28
|
+
when :chrome
|
29
|
+
default_chrome_options
|
30
|
+
when :firefox
|
31
|
+
default_firefox_options
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def configure
|
36
|
+
yield options if block_given?
|
37
|
+
end
|
38
|
+
|
39
|
+
# driver_path is lazily initialized by default. Eagerly set it to avoid race
|
40
|
+
# conditions when using parallel tests.
|
41
|
+
def preload
|
42
|
+
case type
|
43
|
+
when :chrome
|
44
|
+
resolve_driver_path(::Selenium::WebDriver::Chrome)
|
45
|
+
when :firefox
|
46
|
+
resolve_driver_path(::Selenium::WebDriver::Firefox)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
def default_chrome_options
|
52
|
+
options = ::Selenium::WebDriver::Chrome::Options.new
|
53
|
+
options.add_argument("--disable-search-engine-choice-screen")
|
54
|
+
options.add_argument("--headless") if name == :headless_chrome
|
55
|
+
options.add_argument("--disable-gpu") if Gem.win_platform?
|
56
|
+
options
|
57
|
+
end
|
58
|
+
|
59
|
+
def default_firefox_options
|
60
|
+
options = ::Selenium::WebDriver::Firefox::Options.new
|
61
|
+
options.add_argument("-headless") if name == :headless_firefox
|
62
|
+
options
|
63
|
+
end
|
64
|
+
|
65
|
+
def resolve_driver_path(namespace)
|
66
|
+
# The path method has been deprecated in 4.20.0
|
67
|
+
if Gem::Version.new(::Selenium::WebDriver::VERSION) >= Gem::Version.new("4.20.0")
|
68
|
+
namespace::Service.driver_path = ::Selenium::WebDriver::DriverFinder.new(options, namespace::Service.new).driver_path
|
69
|
+
else
|
70
|
+
namespace::Service.driver_path = ::Selenium::WebDriver::DriverFinder.path(options, namespace::Service)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
module ActionDispatch
|
6
|
+
module SystemTesting
|
7
|
+
class Driver # :nodoc:
|
8
|
+
attr_reader :name
|
9
|
+
|
10
|
+
def initialize(driver_type, **options, &capabilities)
|
11
|
+
@driver_type = driver_type
|
12
|
+
@screen_size = options[:screen_size]
|
13
|
+
@options = options[:options] || {}
|
14
|
+
@name = @options.delete(:name) || driver_type
|
15
|
+
@capabilities = capabilities
|
16
|
+
|
17
|
+
if driver_type == :selenium
|
18
|
+
gem "selenium-webdriver", ">= 4.0.0"
|
19
|
+
require "selenium/webdriver"
|
20
|
+
@browser = Browser.new(options[:using])
|
21
|
+
@browser.preload unless @options[:browser] == :remote
|
22
|
+
else
|
23
|
+
@browser = nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def use
|
28
|
+
register if registerable?
|
29
|
+
|
30
|
+
setup
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def registerable?
|
35
|
+
[:selenium, :cuprite, :rack_test, :playwright].include?(@driver_type)
|
36
|
+
end
|
37
|
+
|
38
|
+
def register
|
39
|
+
@browser&.configure(&@capabilities)
|
40
|
+
|
41
|
+
Capybara.register_driver name do |app|
|
42
|
+
case @driver_type
|
43
|
+
when :selenium then register_selenium(app)
|
44
|
+
when :cuprite then register_cuprite(app)
|
45
|
+
when :rack_test then register_rack_test(app)
|
46
|
+
when :playwright then register_playwright(app)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def browser_options
|
52
|
+
@options.merge(options: @browser.options).compact
|
53
|
+
end
|
54
|
+
|
55
|
+
def register_selenium(app)
|
56
|
+
Capybara::Selenium::Driver.new(app, browser: @browser.type, **browser_options).tap do |driver|
|
57
|
+
driver.browser.manage.window.size = Selenium::WebDriver::Dimension.new(*@screen_size)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def register_cuprite(app)
|
62
|
+
Capybara::Cuprite::Driver.new(app, @options.merge(window_size: @screen_size))
|
63
|
+
end
|
64
|
+
|
65
|
+
def register_rack_test(app)
|
66
|
+
Capybara::RackTest::Driver.new(app, respect_data_method: true, **@options)
|
67
|
+
end
|
68
|
+
|
69
|
+
def register_playwright(app)
|
70
|
+
screen = { width: @screen_size[0], height: @screen_size[1] } if @screen_size
|
71
|
+
options = {
|
72
|
+
screen: screen,
|
73
|
+
viewport: screen,
|
74
|
+
**@options
|
75
|
+
}.compact
|
76
|
+
|
77
|
+
Capybara::Playwright::Driver.new(app, **options)
|
78
|
+
end
|
79
|
+
|
80
|
+
def setup
|
81
|
+
Capybara.current_driver = name
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
module ActionDispatch
|
6
|
+
module SystemTesting
|
7
|
+
class Server # :nodoc:
|
8
|
+
class << self
|
9
|
+
attr_accessor :silence_puma
|
10
|
+
end
|
11
|
+
|
12
|
+
self.silence_puma = false
|
13
|
+
|
14
|
+
def run
|
15
|
+
setup
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
def setup
|
20
|
+
set_server
|
21
|
+
set_port
|
22
|
+
end
|
23
|
+
|
24
|
+
def set_server
|
25
|
+
Capybara.server = :puma, { Silent: self.class.silence_puma } if Capybara.server == Capybara.servers[:default]
|
26
|
+
end
|
27
|
+
|
28
|
+
def set_port
|
29
|
+
Capybara.always_include_port = true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
module ActionDispatch
|
6
|
+
module SystemTesting
|
7
|
+
module TestHelpers
|
8
|
+
# Screenshot helper for system testing.
|
9
|
+
module ScreenshotHelper
|
10
|
+
# Takes a screenshot of the current page in the browser.
|
11
|
+
#
|
12
|
+
# `take_screenshot` can be used at any point in your system tests to take a
|
13
|
+
# screenshot of the current state. This can be useful for debugging or
|
14
|
+
# automating visual testing. You can take multiple screenshots per test to
|
15
|
+
# investigate changes at different points during your test. These will be named
|
16
|
+
# with a sequential prefix (or 'failed' for failing tests)
|
17
|
+
#
|
18
|
+
# The default screenshots directory is `tmp/screenshots` but you can set a
|
19
|
+
# different one with `Capybara.save_path`
|
20
|
+
#
|
21
|
+
# You can use the `html` argument or set the
|
22
|
+
# `RAILS_SYSTEM_TESTING_SCREENSHOT_HTML` environment variable to save the HTML
|
23
|
+
# from the page that is being screenshotted so you can investigate the elements
|
24
|
+
# on the page at the time of the screenshot
|
25
|
+
#
|
26
|
+
# You can use the `screenshot` argument or set the
|
27
|
+
# `RAILS_SYSTEM_TESTING_SCREENSHOT` environment variable to control the output.
|
28
|
+
# Possible values are:
|
29
|
+
# `simple` (default)
|
30
|
+
# : Only displays the screenshot path. This is the default value.
|
31
|
+
#
|
32
|
+
# `inline`
|
33
|
+
# : Display the screenshot in the terminal using the iTerm image protocol
|
34
|
+
# (https://iterm2.com/documentation-images.html).
|
35
|
+
#
|
36
|
+
# `artifact`
|
37
|
+
# : Display the screenshot in the terminal, using the terminal artifact
|
38
|
+
# format (https://buildkite.github.io/terminal-to-html/inline-images/).
|
39
|
+
#
|
40
|
+
#
|
41
|
+
def take_screenshot(html: false, screenshot: nil)
|
42
|
+
showing_html = html || html_from_env?
|
43
|
+
|
44
|
+
increment_unique
|
45
|
+
save_html if showing_html
|
46
|
+
save_image
|
47
|
+
show display_image(html: showing_html, screenshot_output: screenshot)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Takes a screenshot of the current page in the browser if the test failed.
|
51
|
+
#
|
52
|
+
# `take_failed_screenshot` is called during system test teardown.
|
53
|
+
def take_failed_screenshot
|
54
|
+
return unless failed? && supports_screenshot? && Capybara::Session.instance_created?
|
55
|
+
|
56
|
+
take_screenshot
|
57
|
+
metadata[:failure_screenshot_path] = relative_image_path if Minitest::Runnable.method_defined?(:metadata)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
attr_accessor :_screenshot_counter
|
62
|
+
|
63
|
+
def html_from_env?
|
64
|
+
ENV["RAILS_SYSTEM_TESTING_SCREENSHOT_HTML"] == "1"
|
65
|
+
end
|
66
|
+
|
67
|
+
def increment_unique
|
68
|
+
@_screenshot_counter ||= 0
|
69
|
+
@_screenshot_counter += 1
|
70
|
+
end
|
71
|
+
|
72
|
+
def unique
|
73
|
+
failed? ? "failures" : (_screenshot_counter || 0).to_s
|
74
|
+
end
|
75
|
+
|
76
|
+
def image_name
|
77
|
+
sanitized_method_name = method_name.gsub(/[^\w]+/, "-")
|
78
|
+
name = "#{unique}_#{sanitized_method_name}"
|
79
|
+
name[0...225]
|
80
|
+
end
|
81
|
+
|
82
|
+
def image_path
|
83
|
+
absolute_image_path.to_s
|
84
|
+
end
|
85
|
+
|
86
|
+
def html_path
|
87
|
+
absolute_html_path.to_s
|
88
|
+
end
|
89
|
+
|
90
|
+
def absolute_path
|
91
|
+
Rails.root.join(screenshots_dir, image_name)
|
92
|
+
end
|
93
|
+
|
94
|
+
def screenshots_dir
|
95
|
+
Capybara.save_path.presence || "tmp/screenshots"
|
96
|
+
end
|
97
|
+
|
98
|
+
def absolute_image_path
|
99
|
+
"#{absolute_path}.png"
|
100
|
+
end
|
101
|
+
|
102
|
+
def relative_image_path
|
103
|
+
"#{absolute_path.relative_path_from(Rails.root)}.png"
|
104
|
+
end
|
105
|
+
|
106
|
+
def absolute_html_path
|
107
|
+
"#{absolute_path}.html"
|
108
|
+
end
|
109
|
+
|
110
|
+
# rubocop:disable Lint/Debugger
|
111
|
+
def save_html
|
112
|
+
page.save_page(absolute_html_path)
|
113
|
+
end
|
114
|
+
|
115
|
+
def save_image
|
116
|
+
page.save_screenshot(absolute_image_path)
|
117
|
+
end
|
118
|
+
# rubocop:enable Lint/Debugger
|
119
|
+
|
120
|
+
def output_type
|
121
|
+
# Environment variables have priority
|
122
|
+
output_type = ENV["RAILS_SYSTEM_TESTING_SCREENSHOT"] || ENV["CAPYBARA_INLINE_SCREENSHOT"]
|
123
|
+
|
124
|
+
# Default to outputting a path to the screenshot
|
125
|
+
output_type ||= "simple"
|
126
|
+
|
127
|
+
output_type
|
128
|
+
end
|
129
|
+
|
130
|
+
def show(img)
|
131
|
+
puts img
|
132
|
+
end
|
133
|
+
|
134
|
+
def display_image(html:, screenshot_output:)
|
135
|
+
message = +"[Screenshot Image]: #{image_path}\n"
|
136
|
+
message << +"[Screenshot HTML]: #{html_path}\n" if html
|
137
|
+
|
138
|
+
case screenshot_output || output_type
|
139
|
+
when "artifact"
|
140
|
+
message << "\e]1338;url=artifact://#{absolute_image_path}\a\n"
|
141
|
+
when "inline"
|
142
|
+
name = inline_base64(File.basename(absolute_image_path))
|
143
|
+
image = inline_base64(File.read(absolute_image_path))
|
144
|
+
message << "\e]1337;File=name=#{name};height=400px;inline=1:#{image}\a\n"
|
145
|
+
end
|
146
|
+
|
147
|
+
message
|
148
|
+
end
|
149
|
+
|
150
|
+
def inline_base64(path)
|
151
|
+
Base64.strict_encode64(path)
|
152
|
+
end
|
153
|
+
|
154
|
+
def failed?
|
155
|
+
!passed? && !skipped?
|
156
|
+
end
|
157
|
+
|
158
|
+
def supports_screenshot?
|
159
|
+
Capybara.current_driver != :rack_test
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
module ActionDispatch
|
6
|
+
module SystemTesting
|
7
|
+
module TestHelpers
|
8
|
+
module SetupAndTeardown # :nodoc:
|
9
|
+
def before_teardown
|
10
|
+
take_failed_screenshot
|
11
|
+
ensure
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
def after_teardown
|
16
|
+
Capybara.reset_sessions!
|
17
|
+
ensure
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
module ActionDispatch
|
6
|
+
# This is a class that abstracts away an asserted response. It purposely does
|
7
|
+
# not inherit from Response because it doesn't need it. That means it does not
|
8
|
+
# have headers or a body.
|
9
|
+
class AssertionResponse
|
10
|
+
attr_reader :code, :name
|
11
|
+
|
12
|
+
GENERIC_RESPONSE_CODES = { # :nodoc:
|
13
|
+
success: "2XX",
|
14
|
+
missing: "404",
|
15
|
+
redirect: "3XX",
|
16
|
+
error: "5XX"
|
17
|
+
}
|
18
|
+
|
19
|
+
# Accepts a specific response status code as an Integer (404) or String ('404')
|
20
|
+
# or a response status range as a Symbol pseudo-code (:success, indicating any
|
21
|
+
# 200-299 status code).
|
22
|
+
def initialize(code_or_name)
|
23
|
+
if code_or_name.is_a?(Symbol)
|
24
|
+
@name = code_or_name
|
25
|
+
@code = code_from_name(code_or_name)
|
26
|
+
else
|
27
|
+
@name = name_from_code(code_or_name)
|
28
|
+
@code = code_or_name
|
29
|
+
end
|
30
|
+
|
31
|
+
raise ArgumentError, "Invalid response name: #{name}" if @code.nil?
|
32
|
+
raise ArgumentError, "Invalid response code: #{code}" if @name.nil?
|
33
|
+
end
|
34
|
+
|
35
|
+
def code_and_name
|
36
|
+
"#{code}: #{name}"
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
def code_from_name(name)
|
41
|
+
GENERIC_RESPONSE_CODES[name] || Rack::Utils.status_code(name)
|
42
|
+
end
|
43
|
+
|
44
|
+
def name_from_code(code)
|
45
|
+
GENERIC_RESPONSE_CODES.invert[code] || Rack::Utils::HTTP_STATUS_CODES[code]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|