actionpack 7.1.5.1 → 8.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +308 -523
  3. data/README.rdoc +1 -1
  4. data/lib/abstract_controller/asset_paths.rb +6 -2
  5. data/lib/abstract_controller/base.rb +104 -105
  6. data/lib/abstract_controller/caching/fragments.rb +50 -53
  7. data/lib/abstract_controller/caching.rb +8 -3
  8. data/lib/abstract_controller/callbacks.rb +70 -62
  9. data/lib/abstract_controller/collector.rb +7 -7
  10. data/lib/abstract_controller/deprecator.rb +2 -0
  11. data/lib/abstract_controller/error.rb +2 -0
  12. data/lib/abstract_controller/helpers.rb +71 -84
  13. data/lib/abstract_controller/logger.rb +4 -1
  14. data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
  15. data/lib/abstract_controller/rendering.rb +13 -13
  16. data/lib/abstract_controller/translation.rb +12 -13
  17. data/lib/abstract_controller/url_for.rb +8 -6
  18. data/lib/abstract_controller.rb +2 -0
  19. data/lib/action_controller/api/api_rendering.rb +2 -0
  20. data/lib/action_controller/api.rb +76 -72
  21. data/lib/action_controller/base.rb +199 -126
  22. data/lib/action_controller/caching.rb +16 -14
  23. data/lib/action_controller/deprecator.rb +2 -0
  24. data/lib/action_controller/form_builder.rb +21 -18
  25. data/lib/action_controller/log_subscriber.rb +23 -2
  26. data/lib/action_controller/metal/allow_browser.rb +133 -0
  27. data/lib/action_controller/metal/basic_implicit_render.rb +2 -0
  28. data/lib/action_controller/metal/conditional_get.rb +217 -175
  29. data/lib/action_controller/metal/content_security_policy.rb +25 -24
  30. data/lib/action_controller/metal/cookies.rb +4 -2
  31. data/lib/action_controller/metal/data_streaming.rb +72 -63
  32. data/lib/action_controller/metal/default_headers.rb +5 -3
  33. data/lib/action_controller/metal/etag_with_flash.rb +3 -1
  34. data/lib/action_controller/metal/etag_with_template_digest.rb +17 -15
  35. data/lib/action_controller/metal/exceptions.rb +16 -9
  36. data/lib/action_controller/metal/flash.rb +13 -14
  37. data/lib/action_controller/metal/head.rb +15 -11
  38. data/lib/action_controller/metal/helpers.rb +63 -55
  39. data/lib/action_controller/metal/http_authentication.rb +209 -201
  40. data/lib/action_controller/metal/implicit_render.rb +17 -15
  41. data/lib/action_controller/metal/instrumentation.rb +16 -14
  42. data/lib/action_controller/metal/live.rb +177 -128
  43. data/lib/action_controller/metal/logging.rb +6 -4
  44. data/lib/action_controller/metal/mime_responds.rb +151 -142
  45. data/lib/action_controller/metal/parameter_encoding.rb +34 -32
  46. data/lib/action_controller/metal/params_wrapper.rb +57 -59
  47. data/lib/action_controller/metal/permissions_policy.rb +22 -12
  48. data/lib/action_controller/metal/rate_limiting.rb +92 -0
  49. data/lib/action_controller/metal/redirecting.rb +213 -94
  50. data/lib/action_controller/metal/renderers.rb +78 -57
  51. data/lib/action_controller/metal/rendering.rb +111 -77
  52. data/lib/action_controller/metal/request_forgery_protection.rb +182 -143
  53. data/lib/action_controller/metal/rescue.rb +20 -9
  54. data/lib/action_controller/metal/streaming.rb +118 -195
  55. data/lib/action_controller/metal/strong_parameters.rb +720 -530
  56. data/lib/action_controller/metal/testing.rb +2 -0
  57. data/lib/action_controller/metal/url_for.rb +17 -15
  58. data/lib/action_controller/metal.rb +86 -60
  59. data/lib/action_controller/railtie.rb +36 -15
  60. data/lib/action_controller/railties/helpers.rb +2 -0
  61. data/lib/action_controller/renderer.rb +41 -36
  62. data/lib/action_controller/structured_event_subscriber.rb +116 -0
  63. data/lib/action_controller/template_assertions.rb +4 -2
  64. data/lib/action_controller/test_case.rb +160 -131
  65. data/lib/action_controller.rb +5 -1
  66. data/lib/action_dispatch/constants.rb +8 -0
  67. data/lib/action_dispatch/deprecator.rb +2 -0
  68. data/lib/action_dispatch/http/cache.rb +163 -35
  69. data/lib/action_dispatch/http/content_disposition.rb +2 -0
  70. data/lib/action_dispatch/http/content_security_policy.rb +54 -39
  71. data/lib/action_dispatch/http/filter_parameters.rb +14 -8
  72. data/lib/action_dispatch/http/filter_redirect.rb +22 -1
  73. data/lib/action_dispatch/http/headers.rb +22 -22
  74. data/lib/action_dispatch/http/mime_negotiation.rb +89 -41
  75. data/lib/action_dispatch/http/mime_type.rb +25 -21
  76. data/lib/action_dispatch/http/mime_types.rb +3 -0
  77. data/lib/action_dispatch/http/param_builder.rb +187 -0
  78. data/lib/action_dispatch/http/param_error.rb +26 -0
  79. data/lib/action_dispatch/http/parameters.rb +14 -12
  80. data/lib/action_dispatch/http/permissions_policy.rb +25 -36
  81. data/lib/action_dispatch/http/query_parser.rb +55 -0
  82. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  83. data/lib/action_dispatch/http/request.rb +141 -92
  84. data/lib/action_dispatch/http/response.rb +137 -77
  85. data/lib/action_dispatch/http/upload.rb +18 -16
  86. data/lib/action_dispatch/http/url.rb +187 -89
  87. data/lib/action_dispatch/journey/formatter.rb +21 -9
  88. data/lib/action_dispatch/journey/gtg/builder.rb +4 -3
  89. data/lib/action_dispatch/journey/gtg/simulator.rb +34 -11
  90. data/lib/action_dispatch/journey/gtg/transition_table.rb +47 -53
  91. data/lib/action_dispatch/journey/nfa/dot.rb +2 -0
  92. data/lib/action_dispatch/journey/nodes/node.rb +8 -6
  93. data/lib/action_dispatch/journey/parser.rb +99 -195
  94. data/lib/action_dispatch/journey/path/pattern.rb +4 -1
  95. data/lib/action_dispatch/journey/route.rb +54 -38
  96. data/lib/action_dispatch/journey/router/utils.rb +22 -27
  97. data/lib/action_dispatch/journey/router.rb +63 -83
  98. data/lib/action_dispatch/journey/routes.rb +11 -2
  99. data/lib/action_dispatch/journey/scanner.rb +46 -42
  100. data/lib/action_dispatch/journey/visitors.rb +57 -23
  101. data/lib/action_dispatch/journey/visualizer/fsm.js +4 -6
  102. data/lib/action_dispatch/journey.rb +2 -0
  103. data/lib/action_dispatch/log_subscriber.rb +7 -1
  104. data/lib/action_dispatch/middleware/actionable_exceptions.rb +2 -0
  105. data/lib/action_dispatch/middleware/assume_ssl.rb +8 -5
  106. data/lib/action_dispatch/middleware/callbacks.rb +3 -1
  107. data/lib/action_dispatch/middleware/cookies.rb +125 -106
  108. data/lib/action_dispatch/middleware/debug_exceptions.rb +37 -8
  109. data/lib/action_dispatch/middleware/debug_locks.rb +15 -13
  110. data/lib/action_dispatch/middleware/debug_view.rb +13 -5
  111. data/lib/action_dispatch/middleware/exception_wrapper.rb +18 -23
  112. data/lib/action_dispatch/middleware/executor.rb +19 -4
  113. data/lib/action_dispatch/middleware/flash.rb +63 -51
  114. data/lib/action_dispatch/middleware/host_authorization.rb +17 -15
  115. data/lib/action_dispatch/middleware/public_exceptions.rb +14 -12
  116. data/lib/action_dispatch/middleware/reloader.rb +5 -3
  117. data/lib/action_dispatch/middleware/remote_ip.rb +87 -77
  118. data/lib/action_dispatch/middleware/request_id.rb +16 -10
  119. data/lib/action_dispatch/middleware/server_timing.rb +4 -2
  120. data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -0
  121. data/lib/action_dispatch/middleware/session/cache_store.rb +30 -8
  122. data/lib/action_dispatch/middleware/session/cookie_store.rb +27 -26
  123. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +7 -3
  124. data/lib/action_dispatch/middleware/show_exceptions.rb +16 -16
  125. data/lib/action_dispatch/middleware/ssl.rb +53 -40
  126. data/lib/action_dispatch/middleware/stack.rb +11 -10
  127. data/lib/action_dispatch/middleware/static.rb +33 -31
  128. data/lib/action_dispatch/middleware/templates/rescues/_copy_button.html.erb +1 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +3 -5
  130. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +9 -5
  131. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +1 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +1 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +4 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +3 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +50 -0
  136. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +1 -0
  137. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -0
  138. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -0
  139. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -0
  140. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -0
  141. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +1 -1
  142. data/lib/action_dispatch/railtie.rb +23 -3
  143. data/lib/action_dispatch/request/session.rb +24 -21
  144. data/lib/action_dispatch/request/utils.rb +11 -3
  145. data/lib/action_dispatch/routing/endpoint.rb +2 -0
  146. data/lib/action_dispatch/routing/inspector.rb +85 -60
  147. data/lib/action_dispatch/routing/mapper.rb +1031 -851
  148. data/lib/action_dispatch/routing/polymorphic_routes.rb +69 -62
  149. data/lib/action_dispatch/routing/redirection.rb +47 -39
  150. data/lib/action_dispatch/routing/route_set.rb +79 -56
  151. data/lib/action_dispatch/routing/routes_proxy.rb +7 -4
  152. data/lib/action_dispatch/routing/url_for.rb +130 -125
  153. data/lib/action_dispatch/routing.rb +150 -148
  154. data/lib/action_dispatch/structured_event_subscriber.rb +20 -0
  155. data/lib/action_dispatch/system_test_case.rb +91 -81
  156. data/lib/action_dispatch/system_testing/browser.rb +16 -23
  157. data/lib/action_dispatch/system_testing/driver.rb +2 -0
  158. data/lib/action_dispatch/system_testing/server.rb +2 -0
  159. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +34 -23
  160. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +2 -0
  161. data/lib/action_dispatch/testing/assertion_response.rb +9 -7
  162. data/lib/action_dispatch/testing/assertions/response.rb +52 -25
  163. data/lib/action_dispatch/testing/assertions/routing.rb +168 -87
  164. data/lib/action_dispatch/testing/assertions.rb +2 -0
  165. data/lib/action_dispatch/testing/integration.rb +233 -223
  166. data/lib/action_dispatch/testing/request_encoder.rb +11 -9
  167. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  168. data/lib/action_dispatch/testing/test_process.rb +11 -8
  169. data/lib/action_dispatch/testing/test_request.rb +3 -1
  170. data/lib/action_dispatch/testing/test_response.rb +27 -26
  171. data/lib/action_dispatch.rb +36 -32
  172. data/lib/action_pack/gem_version.rb +6 -4
  173. data/lib/action_pack/version.rb +3 -1
  174. data/lib/action_pack.rb +17 -16
  175. metadata +36 -32
  176. data/lib/action_dispatch/journey/parser.y +0 -50
  177. data/lib/action_dispatch/journey/parser_extras.rb +0 -31
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  gem "capybara", ">= 3.26"
4
6
 
5
7
  require "capybara/dsl"
@@ -12,103 +14,103 @@ require "action_dispatch/system_testing/test_helpers/screenshot_helper"
12
14
  require "action_dispatch/system_testing/test_helpers/setup_and_teardown"
13
15
 
14
16
  module ActionDispatch
15
- # = System Testing
17
+ # # System Testing
16
18
  #
17
- # System tests let you test applications in the browser. Because system
18
- # tests use a real browser experience, you can test all of your JavaScript
19
- # easily from your test suite.
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.
20
22
  #
21
- # To create a system test in your application, extend your test class
22
- # from <tt>ApplicationSystemTestCase</tt>. System tests use Capybara as a
23
- # base and allow you to configure the settings through your
24
- # <tt>application_system_test_case.rb</tt> file that is generated with a new
25
- # application or scaffold.
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 the `application_system_test_case.rb` file,
26
+ # which is created when you generate your first system test.
26
27
  #
27
28
  # Here is an example system test:
28
29
  #
29
- # require "application_system_test_case"
30
+ # require "application_system_test_case"
30
31
  #
31
- # class Users::CreateTest < ApplicationSystemTestCase
32
- # test "adding a new user" do
33
- # visit users_path
34
- # click_on 'New User'
32
+ # class Users::CreateTest < ApplicationSystemTestCase
33
+ # test "adding a new user" do
34
+ # visit users_path
35
+ # click_on 'New User'
35
36
  #
36
- # fill_in 'Name', with: 'Arya'
37
- # click_on 'Create User'
37
+ # fill_in 'Name', with: 'Arya'
38
+ # click_on 'Create User'
38
39
  #
39
- # assert_text 'Arya'
40
+ # assert_text 'Arya'
41
+ # end
40
42
  # end
41
- # end
42
43
  #
43
- # When generating an application or scaffold, an +application_system_test_case.rb+
44
- # file will also be generated containing the base class for system testing.
45
- # This is where you can change the driver, add Capybara settings, and other
46
- # configuration for your system tests.
44
+ # When generating system tests, 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.
47
48
  #
48
- # require "test_helper"
49
+ # require "test_helper"
49
50
  #
50
- # class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
51
- # driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
52
- # end
51
+ # class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
52
+ # driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
53
+ # end
53
54
  #
54
- # By default, +ActionDispatch::SystemTestCase+ is driven by the
55
- # Selenium driver, with the Chrome browser, and a browser size of 1400x1400.
55
+ # By default, `ActionDispatch::SystemTestCase` is driven by the Selenium driver,
56
+ # with the Chrome browser, and a browser size of 1400x1400.
56
57
  #
57
58
  # Changing the driver configuration options is easy. Let's say you want to use
58
- # the Firefox browser instead of Chrome. In your +application_system_test_case.rb+
59
- # file add the following:
60
- #
61
- # require "test_helper"
59
+ # the Firefox browser instead of Chrome. In your
60
+ # `application_system_test_case.rb` file add the following:
62
61
  #
63
- # class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
64
- # driven_by :selenium, using: :firefox
65
- # end
62
+ # require "test_helper"
66
63
  #
67
- # +driven_by+ has a required argument for the driver name. The keyword
68
- # arguments are +:using+ for the browser and +:screen_size+ to change the
69
- # size of the browser screen. These two options are not applicable for
70
- # headless drivers and will be silently ignored if passed.
71
- #
72
- # Headless browsers such as headless Chrome and headless Firefox are also supported.
73
- # You can use these browsers by setting the +:using+ argument to +:headless_chrome+ or +:headless_firefox+.
64
+ # class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
65
+ # driven_by :selenium, using: :firefox
66
+ # end
74
67
  #
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
77
- # +application_system_test_case.rb+ file. In this case, you would leave out
78
- # the +:using+ option because the driver is headless, but you can still use
79
- # +:screen_size+ to change the size of the browser screen, also you can use
80
- # +:options+ to pass options supported by the driver. Please refer to your
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
81
83
  # driver documentation to learn about supported options.
82
84
  #
83
- # require "test_helper"
84
- # require "capybara/cuprite"
85
+ # require "test_helper"
86
+ # require "capybara/cuprite"
85
87
  #
86
- # class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
87
- # driven_by :cuprite, screen_size: [1400, 1400], options:
88
- # { js_errors: true }
89
- # end
88
+ # class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
89
+ # driven_by :cuprite, screen_size: [1400, 1400], options:
90
+ # { js_errors: true }
91
+ # end
90
92
  #
91
- # Some drivers require browser capabilities to be passed as a block instead
92
- # of through the +options+ hash.
93
+ # Some drivers require browser capabilities to be passed as a block instead of
94
+ # through the `options` hash.
93
95
  #
94
96
  # 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')
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
106
109
  # end
107
- # end
108
110
  #
109
- # Because +ActionDispatch::SystemTestCase+ is a shim between Capybara
110
- # and \Rails, any driver that is supported by Capybara is supported by system
111
- # tests as long as you include the required gems and files.
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.
112
114
  class SystemTestCase < ActiveSupport::TestCase
113
115
  include Capybara::DSL
114
116
  include Capybara::Minitest::Assertions
@@ -137,28 +139,36 @@ module ActionDispatch
137
139
 
138
140
  # System Test configuration options
139
141
  #
140
- # The default settings are Selenium, using Chrome, with a screen size
141
- # of 1400x1400.
142
+ # The default settings are Selenium, using Chrome, with a screen size of
143
+ # 1400x1400.
142
144
  #
143
145
  # Examples:
144
146
  #
145
- # driven_by :cuprite
147
+ # driven_by :cuprite
146
148
  #
147
- # driven_by :selenium, screen_size: [800, 800]
149
+ # driven_by :selenium, screen_size: [800, 800]
148
150
  #
149
- # driven_by :selenium, using: :chrome
151
+ # driven_by :selenium, using: :chrome
150
152
  #
151
- # driven_by :selenium, using: :headless_chrome
153
+ # driven_by :selenium, using: :headless_chrome
152
154
  #
153
- # driven_by :selenium, using: :firefox
155
+ # driven_by :selenium, using: :firefox
154
156
  #
155
- # driven_by :selenium, using: :headless_firefox
157
+ # driven_by :selenium, using: :headless_firefox
156
158
  def self.driven_by(driver, using: :chrome, screen_size: [1400, 1400], options: {}, &capabilities)
157
159
  driver_options = { using: using, screen_size: screen_size, options: options }
158
160
 
159
161
  self.driver = SystemTesting::Driver.new(driver, **driver_options, &capabilities)
160
162
  end
161
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
+
162
172
  private
163
173
  def url_helpers
164
174
  @url_helpers ||=
@@ -178,9 +188,9 @@ module ActionDispatch
178
188
  end
179
189
  end
180
190
 
181
- def method_missing(name, *args, &block)
191
+ def method_missing(name, ...)
182
192
  if url_helpers.respond_to?(name)
183
- url_helpers.public_send(name, *args, &block)
193
+ url_helpers.public_send(name, ...)
184
194
  else
185
195
  super
186
196
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionDispatch
4
6
  module SystemTesting
5
7
  class Browser # :nodoc:
@@ -7,7 +9,6 @@ module ActionDispatch
7
9
 
8
10
  def initialize(name)
9
11
  @name = name
10
- set_default_options
11
12
  end
12
13
 
13
14
  def type
@@ -25,9 +26,9 @@ module ActionDispatch
25
26
  @options ||=
26
27
  case type
27
28
  when :chrome
28
- ::Selenium::WebDriver::Chrome::Options.new
29
+ default_chrome_options
29
30
  when :firefox
30
- ::Selenium::WebDriver::Firefox::Options.new
31
+ default_firefox_options
31
32
  end
32
33
  end
33
34
 
@@ -35,8 +36,8 @@ module ActionDispatch
35
36
  yield options if block_given?
36
37
  end
37
38
 
38
- # driver_path is lazily initialized by default. Eagerly set it to
39
- # avoid race conditions when using parallel tests.
39
+ # driver_path is lazily initialized by default. Eagerly set it to avoid race
40
+ # conditions when using parallel tests.
40
41
  def preload
41
42
  case type
42
43
  when :chrome
@@ -47,26 +48,18 @@ module ActionDispatch
47
48
  end
48
49
 
49
50
  private
50
- def set_default_options
51
- case name
52
- when :headless_chrome
53
- set_headless_chrome_browser_options
54
- when :headless_firefox
55
- set_headless_firefox_browser_options
56
- end
57
- end
58
-
59
- def set_headless_chrome_browser_options
60
- configure do |capabilities|
61
- capabilities.add_argument("--headless")
62
- capabilities.add_argument("--disable-gpu") if Gem.win_platform?
63
- end
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
64
57
  end
65
58
 
66
- def set_headless_firefox_browser_options
67
- configure do |capabilities|
68
- capabilities.add_argument("-headless")
69
- end
59
+ def default_firefox_options
60
+ options = ::Selenium::WebDriver::Firefox::Options.new
61
+ options.add_argument("-headless") if name == :headless_firefox
62
+ options
70
63
  end
71
64
 
72
65
  def resolve_driver_path(namespace)
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionDispatch
4
6
  module SystemTesting
5
7
  class Driver # :nodoc:
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionDispatch
4
6
  module SystemTesting
5
7
  class Server # :nodoc:
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionDispatch
4
6
  module SystemTesting
5
7
  module TestHelpers
@@ -7,27 +9,35 @@ module ActionDispatch
7
9
  module ScreenshotHelper
8
10
  # Takes a screenshot of the current page in the browser.
9
11
  #
10
- # +take_screenshot+ can be used at any point in your system tests to take
11
- # a screenshot of the current state. This can be useful for debugging or
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)
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).
15
35
  #
16
- # The default screenshots directory is +tmp/screenshots+ but you can set a different
17
- # one with +Capybara.save_path+
36
+ # `artifact`
37
+ # : Display the screenshot in the terminal, using the terminal artifact
38
+ # format (https://buildkite.github.io/terminal-to-html/inline-images/).
18
39
  #
19
- # You can use the +html+ argument or set the +RAILS_SYSTEM_TESTING_SCREENSHOT_HTML+
20
- # environment variable to save the HTML from the page that is being screenshotted
21
- # so you can investigate the elements on the page at the time of the screenshot
22
40
  #
23
- # You can use the +screenshot+ argument or set the +RAILS_SYSTEM_TESTING_SCREENSHOT+
24
- # environment variable to control the output. Possible values are:
25
- # * [+simple+ (default)] Only displays the screenshot path.
26
- # This is the default value.
27
- # * [+inline+] Display the screenshot in the terminal using the
28
- # iTerm image protocol (https://iterm2.com/documentation-images.html).
29
- # * [+artifact+] Display the screenshot in the terminal, using the terminal
30
- # artifact format (https://buildkite.github.io/terminal-to-html/inline-images/).
31
41
  def take_screenshot(html: false, screenshot: nil)
32
42
  showing_html = html || html_from_env?
33
43
 
@@ -37,10 +47,9 @@ module ActionDispatch
37
47
  show display_image(html: showing_html, screenshot_output: screenshot)
38
48
  end
39
49
 
40
- # Takes a screenshot of the current page in the browser if the test
41
- # failed.
50
+ # Takes a screenshot of the current page in the browser if the test failed.
42
51
  #
43
- # +take_failed_screenshot+ is called during system test teardown.
52
+ # `take_failed_screenshot` is called during system test teardown.
44
53
  def take_failed_screenshot
45
54
  return unless failed? && supports_screenshot? && Capybara::Session.instance_created?
46
55
 
@@ -98,6 +107,7 @@ module ActionDispatch
98
107
  "#{absolute_path}.html"
99
108
  end
100
109
 
110
+ # rubocop:disable Lint/Debugger
101
111
  def save_html
102
112
  page.save_page(absolute_html_path)
103
113
  end
@@ -105,6 +115,7 @@ module ActionDispatch
105
115
  def save_image
106
116
  page.save_screenshot(absolute_image_path)
107
117
  end
118
+ # rubocop:enable Lint/Debugger
108
119
 
109
120
  def output_type
110
121
  # Environment variables have priority
@@ -121,8 +132,8 @@ module ActionDispatch
121
132
  end
122
133
 
123
134
  def display_image(html:, screenshot_output:)
124
- message = +"[Screenshot Image]: #{image_path}\n"
125
- message << +"[Screenshot HTML]: #{html_path}\n" if html
135
+ message = +"[Screenshot Image]: #{image_path} \n"
136
+ message << +"[Screenshot HTML]: #{html_path} \n" if html
126
137
 
127
138
  case screenshot_output || output_type
128
139
  when "artifact"
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionDispatch
4
6
  module SystemTesting
5
7
  module TestHelpers
@@ -1,9 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionDispatch
4
- # This is a class that abstracts away an asserted response. It purposely
5
- # does not inherit from Response because it doesn't need it. That means it
6
- # does not have headers or a body.
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.
7
9
  class AssertionResponse
8
10
  attr_reader :code, :name
9
11
 
@@ -14,9 +16,9 @@ module ActionDispatch
14
16
  error: "5XX"
15
17
  }
16
18
 
17
- # Accepts a specific response status code as an Integer (404) or String
18
- # ('404') or a response status range as a Symbol pseudo-code (:success,
19
- # indicating any 200-299 status code).
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).
20
22
  def initialize(code_or_name)
21
23
  if code_or_name.is_a?(Symbol)
22
24
  @name = code_or_name
@@ -36,7 +38,7 @@ module ActionDispatch
36
38
 
37
39
  private
38
40
  def code_from_name(name)
39
- GENERIC_RESPONSE_CODES[name] || Rack::Utils.status_code(name)
41
+ GENERIC_RESPONSE_CODES[name] || ActionDispatch::Response.rack_status_code(name)
40
42
  end
41
43
 
42
44
  def name_from_code(code)
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionDispatch
4
6
  module Assertions
5
- # A small suite of assertions that test responses from \Rails applications.
7
+ # A small suite of assertions that test responses from Rails applications.
6
8
  module ResponseAssertions
7
9
  RESPONSE_PREDICATES = { # :nodoc:
8
10
  success: :successful?,
@@ -13,20 +15,21 @@ module ActionDispatch
13
15
 
14
16
  # Asserts that the response is one of the following types:
15
17
  #
16
- # * <tt>:success</tt> - Status code was in the 200-299 range
17
- # * <tt>:redirect</tt> - Status code was in the 300-399 range
18
- # * <tt>:missing</tt> - Status code was 404
19
- # * <tt>:error</tt> - Status code was in the 500-599 range
18
+ # * `:success` - Status code was in the 200-299 range
19
+ # * `:redirect` - Status code was in the 300-399 range
20
+ # * `:missing` - Status code was 404
21
+ # * `:error` - Status code was in the 500-599 range
22
+ #
20
23
  #
21
- # You can also pass an explicit status number like <tt>assert_response(501)</tt>
22
- # or its symbolic equivalent <tt>assert_response(:not_implemented)</tt>.
23
- # See +Rack::Utils::SYMBOL_TO_STATUS_CODE+ for a full list.
24
+ # You can also pass an explicit status number like `assert_response(501)` or its
25
+ # symbolic equivalent `assert_response(:not_implemented)`. See
26
+ # `Rack::Utils::SYMBOL_TO_STATUS_CODE` for a full list.
24
27
  #
25
- # # Asserts that the response was a redirection
26
- # assert_response :redirect
28
+ # # Asserts that the response was a redirection
29
+ # assert_response :redirect
27
30
  #
28
- # # Asserts that the response code was status code 401 (unauthorized)
29
- # assert_response 401
31
+ # # Asserts that the response code was status code 401 (unauthorized)
32
+ # assert_response 401
30
33
  def assert_response(type, message = nil)
31
34
  message ||= generate_response_message(type)
32
35
 
@@ -39,21 +42,21 @@ module ActionDispatch
39
42
 
40
43
  # Asserts that the response is a redirect to a URL matching the given options.
41
44
  #
42
- # # Asserts that the redirection was to the "index" action on the WeblogController
43
- # assert_redirected_to controller: "weblog", action: "index"
45
+ # # Asserts that the redirection was to the "index" action on the WeblogController
46
+ # assert_redirected_to controller: "weblog", action: "index"
44
47
  #
45
- # # Asserts that the redirection was to the named route login_url
46
- # assert_redirected_to login_url
48
+ # # Asserts that the redirection was to the named route login_url
49
+ # assert_redirected_to login_url
47
50
  #
48
- # # Asserts that the redirection was to the URL for @customer
49
- # assert_redirected_to @customer
51
+ # # Asserts that the redirection was to the URL for @customer
52
+ # assert_redirected_to @customer
50
53
  #
51
- # # Asserts that the redirection matches the regular expression
52
- # assert_redirected_to %r(\Ahttp://example.org)
54
+ # # Asserts that the redirection matches the regular expression
55
+ # assert_redirected_to %r(\Ahttp://example.org)
53
56
  #
54
- # # Asserts that the redirection has the HTTP status code 301 (Moved
55
- # # Permanently).
56
- # assert_redirected_to "/some/path", status: :moved_permanently
57
+ # # Asserts that the redirection has the HTTP status code 301 (Moved
58
+ # # Permanently).
59
+ # assert_redirected_to "/some/path", status: :moved_permanently
57
60
  def assert_redirected_to(url_options = {}, options = {}, message = nil)
58
61
  options, message = {}, options unless options.is_a?(Hash)
59
62
 
@@ -68,6 +71,20 @@ module ActionDispatch
68
71
  assert_operator redirect_expected, :===, redirect_is, message
69
72
  end
70
73
 
74
+ # Asserts that the given +text+ is present somewhere in the response body.
75
+ #
76
+ # assert_in_body fixture(:name).description
77
+ def assert_in_body(text)
78
+ assert_match(/#{Regexp.escape(text)}/, @response.body)
79
+ end
80
+
81
+ # Asserts that the given +text+ is not present anywhere in the response body.
82
+ #
83
+ # assert_not_in_body fixture(:name).description
84
+ def assert_not_in_body(text)
85
+ assert_no_match(/#{Regexp.escape(text)}/, @response.body)
86
+ end
87
+
71
88
  private
72
89
  # Proxy to to_param if the object will respond to it.
73
90
  def parameterize(value)
@@ -84,8 +101,13 @@ module ActionDispatch
84
101
  end
85
102
 
86
103
  def generate_response_message(expected, actual = @response.response_code)
87
- (+"Expected response to be a <#{code_with_name(expected)}>,"\
88
- " but was a <#{code_with_name(actual)}>").concat(location_if_redirected).concat(response_body_if_short)
104
+ lambda do
105
+ (+"Expected response to be a <#{code_with_name(expected)}>,"\
106
+ " but was a <#{code_with_name(actual)}>").
107
+ concat(location_if_redirected).
108
+ concat(exception_if_present).
109
+ concat(response_body_if_short)
110
+ end
89
111
  end
90
112
 
91
113
  def response_body_if_short
@@ -93,6 +115,11 @@ module ActionDispatch
93
115
  "\nResponse body: #{@response.body}"
94
116
  end
95
117
 
118
+ def exception_if_present
119
+ return "" unless ex = @request&.env&.[]("action_dispatch.exception")
120
+ "\n\nException while processing request: #{Minitest::UnexpectedError.new(ex).message}\n"
121
+ end
122
+
96
123
  def location_if_redirected
97
124
  return "" unless @response.redirection? && @response.location.present?
98
125
  location = normalize_argument_to_redirection(@response.location)