omg-actionpack 8.0.0.alpha1
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.
- 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
|