actionpack 4.2.8 → 5.2.4.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (166) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +285 -444
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -7
  5. data/lib/abstract_controller.rb +12 -5
  6. data/lib/abstract_controller/asset_paths.rb +2 -0
  7. data/lib/abstract_controller/base.rb +45 -49
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/{action_controller → abstract_controller}/caching/fragments.rb +78 -15
  10. data/lib/abstract_controller/callbacks.rb +47 -31
  11. data/lib/abstract_controller/collector.rb +8 -11
  12. data/lib/abstract_controller/error.rb +6 -0
  13. data/lib/abstract_controller/helpers.rb +25 -25
  14. data/lib/abstract_controller/logger.rb +2 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +4 -2
  16. data/lib/abstract_controller/rendering.rb +42 -41
  17. data/lib/abstract_controller/translation.rb +10 -7
  18. data/lib/abstract_controller/url_for.rb +2 -0
  19. data/lib/action_controller.rb +29 -21
  20. data/lib/action_controller/api.rb +149 -0
  21. data/lib/action_controller/api/api_rendering.rb +16 -0
  22. data/lib/action_controller/base.rb +27 -19
  23. data/lib/action_controller/caching.rb +14 -57
  24. data/lib/action_controller/form_builder.rb +50 -0
  25. data/lib/action_controller/log_subscriber.rb +10 -15
  26. data/lib/action_controller/metal.rb +98 -83
  27. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  28. data/lib/action_controller/metal/conditional_get.rb +118 -44
  29. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  30. data/lib/action_controller/metal/cookies.rb +3 -3
  31. data/lib/action_controller/metal/data_streaming.rb +27 -46
  32. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  33. data/lib/action_controller/metal/etag_with_template_digest.rb +20 -13
  34. data/lib/action_controller/metal/exceptions.rb +8 -14
  35. data/lib/action_controller/metal/flash.rb +4 -3
  36. data/lib/action_controller/metal/force_ssl.rb +23 -21
  37. data/lib/action_controller/metal/head.rb +21 -19
  38. data/lib/action_controller/metal/helpers.rb +24 -14
  39. data/lib/action_controller/metal/http_authentication.rb +64 -57
  40. data/lib/action_controller/metal/implicit_render.rb +62 -8
  41. data/lib/action_controller/metal/instrumentation.rb +19 -21
  42. data/lib/action_controller/metal/live.rb +90 -106
  43. data/lib/action_controller/metal/mime_responds.rb +33 -46
  44. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  45. data/lib/action_controller/metal/params_wrapper.rb +61 -53
  46. data/lib/action_controller/metal/redirecting.rb +49 -28
  47. data/lib/action_controller/metal/renderers.rb +87 -44
  48. data/lib/action_controller/metal/rendering.rb +72 -50
  49. data/lib/action_controller/metal/request_forgery_protection.rb +203 -92
  50. data/lib/action_controller/metal/rescue.rb +9 -16
  51. data/lib/action_controller/metal/streaming.rb +12 -10
  52. data/lib/action_controller/metal/strong_parameters.rb +582 -165
  53. data/lib/action_controller/metal/testing.rb +2 -17
  54. data/lib/action_controller/metal/url_for.rb +19 -10
  55. data/lib/action_controller/railtie.rb +28 -10
  56. data/lib/action_controller/railties/helpers.rb +2 -0
  57. data/lib/action_controller/renderer.rb +117 -0
  58. data/lib/action_controller/template_assertions.rb +11 -0
  59. data/lib/action_controller/test_case.rb +280 -411
  60. data/lib/action_dispatch.rb +27 -19
  61. data/lib/action_dispatch/http/cache.rb +93 -47
  62. data/lib/action_dispatch/http/content_security_policy.rb +272 -0
  63. data/lib/action_dispatch/http/filter_parameters.rb +26 -20
  64. data/lib/action_dispatch/http/filter_redirect.rb +10 -11
  65. data/lib/action_dispatch/http/headers.rb +55 -22
  66. data/lib/action_dispatch/http/mime_negotiation.rb +60 -41
  67. data/lib/action_dispatch/http/mime_type.rb +134 -121
  68. data/lib/action_dispatch/http/mime_types.rb +20 -6
  69. data/lib/action_dispatch/http/parameter_filter.rb +25 -11
  70. data/lib/action_dispatch/http/parameters.rb +98 -39
  71. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  72. data/lib/action_dispatch/http/request.rb +200 -118
  73. data/lib/action_dispatch/http/response.rb +225 -110
  74. data/lib/action_dispatch/http/upload.rb +12 -6
  75. data/lib/action_dispatch/http/url.rb +110 -28
  76. data/lib/action_dispatch/journey.rb +7 -5
  77. data/lib/action_dispatch/journey/formatter.rb +55 -32
  78. data/lib/action_dispatch/journey/gtg/builder.rb +7 -5
  79. data/lib/action_dispatch/journey/gtg/simulator.rb +3 -9
  80. data/lib/action_dispatch/journey/gtg/transition_table.rb +17 -16
  81. data/lib/action_dispatch/journey/nfa/builder.rb +5 -3
  82. data/lib/action_dispatch/journey/nfa/dot.rb +13 -13
  83. data/lib/action_dispatch/journey/nfa/simulator.rb +3 -1
  84. data/lib/action_dispatch/journey/nfa/transition_table.rb +5 -48
  85. data/lib/action_dispatch/journey/nodes/node.rb +18 -6
  86. data/lib/action_dispatch/journey/parser.rb +23 -22
  87. data/lib/action_dispatch/journey/parser.y +3 -2
  88. data/lib/action_dispatch/journey/parser_extras.rb +12 -4
  89. data/lib/action_dispatch/journey/path/pattern.rb +50 -44
  90. data/lib/action_dispatch/journey/route.rb +106 -28
  91. data/lib/action_dispatch/journey/router.rb +35 -23
  92. data/lib/action_dispatch/journey/router/utils.rb +20 -11
  93. data/lib/action_dispatch/journey/routes.rb +18 -16
  94. data/lib/action_dispatch/journey/scanner.rb +18 -15
  95. data/lib/action_dispatch/journey/visitors.rb +99 -52
  96. data/lib/action_dispatch/middleware/callbacks.rb +1 -2
  97. data/lib/action_dispatch/middleware/cookies.rb +304 -193
  98. data/lib/action_dispatch/middleware/debug_exceptions.rb +152 -57
  99. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  100. data/lib/action_dispatch/middleware/exception_wrapper.rb +68 -69
  101. data/lib/action_dispatch/middleware/executor.rb +21 -0
  102. data/lib/action_dispatch/middleware/flash.rb +78 -54
  103. data/lib/action_dispatch/middleware/public_exceptions.rb +27 -25
  104. data/lib/action_dispatch/middleware/reloader.rb +5 -91
  105. data/lib/action_dispatch/middleware/remote_ip.rb +41 -31
  106. data/lib/action_dispatch/middleware/request_id.rb +17 -9
  107. data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -25
  108. data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
  109. data/lib/action_dispatch/middleware/session/cookie_store.rb +72 -67
  110. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
  111. data/lib/action_dispatch/middleware/show_exceptions.rb +26 -22
  112. data/lib/action_dispatch/middleware/ssl.rb +114 -36
  113. data/lib/action_dispatch/middleware/stack.rb +31 -44
  114. data/lib/action_dispatch/middleware/static.rb +57 -50
  115. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
  116. data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
  117. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +1 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
  122. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  123. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  124. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +64 -64
  125. data/lib/action_dispatch/railtie.rb +19 -11
  126. data/lib/action_dispatch/request/session.rb +106 -59
  127. data/lib/action_dispatch/request/utils.rb +67 -24
  128. data/lib/action_dispatch/routing.rb +17 -18
  129. data/lib/action_dispatch/routing/endpoint.rb +9 -2
  130. data/lib/action_dispatch/routing/inspector.rb +58 -67
  131. data/lib/action_dispatch/routing/mapper.rb +734 -447
  132. data/lib/action_dispatch/routing/polymorphic_routes.rb +161 -139
  133. data/lib/action_dispatch/routing/redirection.rb +36 -26
  134. data/lib/action_dispatch/routing/route_set.rb +321 -291
  135. data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
  136. data/lib/action_dispatch/routing/url_for.rb +65 -25
  137. data/lib/action_dispatch/system_test_case.rb +147 -0
  138. data/lib/action_dispatch/system_testing/browser.rb +49 -0
  139. data/lib/action_dispatch/system_testing/driver.rb +59 -0
  140. data/lib/action_dispatch/system_testing/server.rb +31 -0
  141. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
  142. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
  143. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  144. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  145. data/lib/action_dispatch/testing/assertions.rb +6 -4
  146. data/lib/action_dispatch/testing/assertions/response.rb +45 -20
  147. data/lib/action_dispatch/testing/assertions/routing.rb +30 -26
  148. data/lib/action_dispatch/testing/integration.rb +347 -209
  149. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  150. data/lib/action_dispatch/testing/test_process.rb +28 -22
  151. data/lib/action_dispatch/testing/test_request.rb +27 -34
  152. data/lib/action_dispatch/testing/test_response.rb +35 -7
  153. data/lib/action_pack.rb +4 -2
  154. data/lib/action_pack/gem_version.rb +5 -3
  155. data/lib/action_pack/version.rb +3 -1
  156. metadata +56 -39
  157. data/lib/action_controller/metal/hide_actions.rb +0 -40
  158. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  159. data/lib/action_controller/middleware.rb +0 -39
  160. data/lib/action_controller/model_naming.rb +0 -12
  161. data/lib/action_dispatch/journey/backwards.rb +0 -5
  162. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  163. data/lib/action_dispatch/middleware/params_parser.rb +0 -60
  164. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  165. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  166. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,4 +1,6 @@
1
- require 'active_support/core_ext/array/extract_options'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/array/extract_options"
2
4
 
3
5
  module ActionDispatch
4
6
  module Routing
@@ -8,9 +10,10 @@ module ActionDispatch
8
10
  attr_accessor :scope, :routes
9
11
  alias :_routes :routes
10
12
 
11
- def initialize(routes, scope, helpers)
13
+ def initialize(routes, scope, helpers, script_namer = nil)
12
14
  @routes, @scope = routes, scope
13
15
  @helpers = helpers
16
+ @script_namer = script_namer
14
17
  end
15
18
 
16
19
  def url_options
@@ -19,7 +22,8 @@ module ActionDispatch
19
22
  end
20
23
  end
21
24
 
22
- def respond_to?(method, include_private = false)
25
+ private
26
+ def respond_to_missing?(method, _)
23
27
  super || @helpers.respond_to?(method)
24
28
  end
25
29
 
@@ -28,15 +32,38 @@ module ActionDispatch
28
32
  self.class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
29
33
  def #{method}(*args)
30
34
  options = args.extract_options!
31
- args << url_options.merge((options || {}).symbolize_keys)
35
+ options = url_options.merge((options || {}).symbolize_keys)
36
+
37
+ if @script_namer
38
+ options[:script_name] = merge_script_names(
39
+ options[:script_name],
40
+ @script_namer.call(options)
41
+ )
42
+ end
43
+
44
+ args << options
32
45
  @helpers.#{method}(*args)
33
46
  end
34
47
  RUBY
35
- send(method, *args)
48
+ public_send(method, *args)
36
49
  else
37
50
  super
38
51
  end
39
52
  end
53
+
54
+ # Keeps the part of the script name provided by the global
55
+ # context via ENV["SCRIPT_NAME"], which `mount` doesn't know
56
+ # about since it depends on the specific request, but use our
57
+ # script name resolver for the mount point dependent part.
58
+ def merge_script_names(previous_script_name, new_script_name)
59
+ return new_script_name unless previous_script_name
60
+
61
+ resolved_parts = new_script_name.count("/")
62
+ previous_parts = previous_script_name.count("/")
63
+ context_parts = previous_parts - resolved_parts + 1
64
+
65
+ (previous_script_name.split("/").slice(0, context_parts).join("/")) + new_script_name
66
+ end
40
67
  end
41
68
  end
42
69
  end
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionDispatch
2
4
  module Routing
3
5
  # In <tt>config/routes.rb</tt> you define URL-to-controller mappings, but the reverse
4
- # is also possible: an URL can be generated from one of your routing definitions.
6
+ # is also possible: a URL can be generated from one of your routing definitions.
5
7
  # URL generation functionality is centralized in this module.
6
8
  #
7
9
  # See ActionDispatch::Routing for general information about routing and routes.rb.
@@ -52,9 +54,11 @@ module ActionDispatch
52
54
  # argument.
53
55
  #
54
56
  # For convenience reasons, mailers provide a shortcut for ActionController::UrlFor#url_for.
55
- # So within mailers, you only have to type 'url_for' instead of 'ActionController::UrlFor#url_for'
56
- # in full. However, mailers don't have hostname information, and that's why you'll still
57
- # have to specify the <tt>:host</tt> argument when generating URLs in mailers.
57
+ # So within mailers, you only have to type +url_for+ instead of 'ActionController::UrlFor#url_for'
58
+ # in full. However, mailers don't have hostname information, and you still have to provide
59
+ # the +:host+ argument or set the default host that will be used in all mailers using the
60
+ # configuration option +config.action_mailer.default_url_options+. For more information on
61
+ # url_for in mailers read the ActionMailer#Base documentation.
58
62
  #
59
63
  #
60
64
  # == URL generation for named routes
@@ -105,16 +109,16 @@ module ActionDispatch
105
109
  end
106
110
 
107
111
  # Hook overridden in controller to add request information
108
- # with `default_url_options`. Application logic should not
112
+ # with +default_url_options+. Application logic should not
109
113
  # go into url_options.
110
114
  def url_options
111
115
  default_url_options
112
116
  end
113
117
 
114
- # Generate a url based on the options provided, default_url_options and the
118
+ # Generate a URL based on the options provided, default_url_options and the
115
119
  # routes defined in routes.rb. The following options are supported:
116
120
  #
117
- # * <tt>:only_path</tt> - If true, the relative url is returned. Defaults to +false+.
121
+ # * <tt>:only_path</tt> - If true, the relative URL is returned. Defaults to +false+.
118
122
  # * <tt>:protocol</tt> - The protocol to connect to. Defaults to 'http'.
119
123
  # * <tt>:host</tt> - Specifies the host the link should be targeted at.
120
124
  # If <tt>:only_path</tt> is false, this option must be
@@ -147,14 +151,32 @@ module ActionDispatch
147
151
  # # => 'http://somehost.org/myapp/tasks/testing'
148
152
  # url_for controller: 'tasks', action: 'testing', host: 'somehost.org', script_name: "/myapp", only_path: true
149
153
  # # => '/myapp/tasks/testing'
154
+ #
155
+ # Missing routes keys may be filled in from the current request's parameters
156
+ # (e.g. +:controller+, +:action+, +:id+ and any other parameters that are
157
+ # placed in the path). Given that the current action has been reached
158
+ # through <tt>GET /users/1</tt>:
159
+ #
160
+ # url_for(only_path: true) # => '/users/1'
161
+ # url_for(only_path: true, action: 'edit') # => '/users/1/edit'
162
+ # url_for(only_path: true, action: 'edit', id: 2) # => '/users/2/edit'
163
+ #
164
+ # Notice that no +:id+ parameter was provided to the first +url_for+ call
165
+ # and the helper used the one from the route's path. Any path parameter
166
+ # implicitly used by +url_for+ can always be overwritten like shown on the
167
+ # last +url_for+ calls.
150
168
  def url_for(options = nil)
169
+ full_url_for(options)
170
+ end
171
+
172
+ def full_url_for(options = nil) # :nodoc:
151
173
  case options
152
174
  when nil
153
175
  _routes.url_for(url_options.symbolize_keys)
154
- when Hash
176
+ when Hash, ActionController::Parameters
155
177
  route_name = options.delete :use_route
156
- _routes.url_for(options.symbolize_keys.reverse_merge!(url_options),
157
- route_name)
178
+ merged_url_options = options.to_h.symbolize_keys.reverse_merge!(url_options)
179
+ _routes.url_for(merged_url_options, route_name)
158
180
  when String
159
181
  options
160
182
  when Symbol
@@ -169,27 +191,45 @@ module ActionDispatch
169
191
  end
170
192
  end
171
193
 
172
- protected
173
-
174
- def optimize_routes_generation?
175
- _routes.optimize_routes_generation? && default_url_options.empty?
194
+ # Allows calling direct or regular named route.
195
+ #
196
+ # resources :buckets
197
+ #
198
+ # direct :recordable do |recording|
199
+ # route_for(:bucket, recording.bucket)
200
+ # end
201
+ #
202
+ # direct :threadable do |threadable|
203
+ # route_for(:recordable, threadable.parent)
204
+ # end
205
+ #
206
+ # This maintains the context of the original caller on
207
+ # whether to return a path or full URL, e.g:
208
+ #
209
+ # threadable_path(threadable) # => "/buckets/1"
210
+ # threadable_url(threadable) # => "http://example.com/buckets/1"
211
+ #
212
+ def route_for(name, *args)
213
+ public_send(:"#{name}_url", *args)
176
214
  end
177
215
 
178
- def _with_routes(routes)
179
- old_routes, @_routes = @_routes, routes
180
- yield
181
- ensure
182
- @_routes = old_routes
183
- end
216
+ protected
184
217
 
185
- def _routes_context
186
- self
187
- end
218
+ def optimize_routes_generation?
219
+ _routes.optimize_routes_generation? && default_url_options.empty?
220
+ end
188
221
 
189
222
  private
190
223
 
191
- def _generate_paths_by_default
192
- true
224
+ def _with_routes(routes) # :doc:
225
+ old_routes, @_routes = @_routes, routes
226
+ yield
227
+ ensure
228
+ @_routes = old_routes
229
+ end
230
+
231
+ def _routes_context # :doc:
232
+ self
193
233
  end
194
234
  end
195
235
  end
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ gem "capybara", ">= 2.15"
4
+
5
+ require "capybara/dsl"
6
+ require "capybara/minitest"
7
+ require "action_controller"
8
+ require "action_dispatch/system_testing/driver"
9
+ require "action_dispatch/system_testing/browser"
10
+ require "action_dispatch/system_testing/server"
11
+ require "action_dispatch/system_testing/test_helpers/screenshot_helper"
12
+ require "action_dispatch/system_testing/test_helpers/setup_and_teardown"
13
+ require "action_dispatch/system_testing/test_helpers/undef_methods"
14
+
15
+ module ActionDispatch
16
+ # = System Testing
17
+ #
18
+ # System tests let you test applications in the browser. Because system
19
+ # tests use a real browser experience, you can test all of your JavaScript
20
+ # easily from your test suite.
21
+ #
22
+ # To create a system test in your application, extend your test class
23
+ # from <tt>ApplicationSystemTestCase</tt>. System tests use Capybara as a
24
+ # base and allow you to configure the settings through your
25
+ # <tt>application_system_test_case.rb</tt> file that is generated with a new
26
+ # 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 +application_system_test_case.rb+
45
+ # file will also be generated containing the base class for system testing.
46
+ # This is where you can change the driver, add Capybara settings, and other
47
+ # 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, <tt>ActionDispatch::SystemTestCase</tt> is driven by the
56
+ # Selenium driver, 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 +application_system_test_case.rb+
60
+ # 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
69
+ # arguments are +:using+ for the browser and +:screen_size+ to change the
70
+ # size of the browser screen. These two options are not applicable for
71
+ # headless drivers and will be silently ignored if passed.
72
+ #
73
+ # Headless browsers such as headless Chrome and headless Firefox are also supported.
74
+ # You can use these browsers by setting the +:using+ argument to +:headless_chrome+ or +:headless_firefox+.
75
+ #
76
+ # To use a headless driver, like Poltergeist, update your Gemfile to use
77
+ # Poltergeist instead of Selenium and then declare the driver name in the
78
+ # +application_system_test_case.rb+ file. In this case, you would leave out
79
+ # the +:using+ option because the driver is headless, but you can still use
80
+ # +:screen_size+ to change the size of the browser screen, also you can use
81
+ # +:options+ to pass options supported by the driver. Please refer to your
82
+ # driver documentation to learn about supported options.
83
+ #
84
+ # require "test_helper"
85
+ # require "capybara/poltergeist"
86
+ #
87
+ # class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
88
+ # driven_by :poltergeist, screen_size: [1400, 1400], options:
89
+ # { js_errors: true }
90
+ # end
91
+ #
92
+ # Because <tt>ActionDispatch::SystemTestCase</tt> is a shim between Capybara
93
+ # and Rails, any driver that is supported by Capybara is supported by system
94
+ # tests as long as you include the required gems and files.
95
+ class SystemTestCase < IntegrationTest
96
+ include Capybara::DSL
97
+ include Capybara::Minitest::Assertions
98
+ include SystemTesting::TestHelpers::SetupAndTeardown
99
+ include SystemTesting::TestHelpers::ScreenshotHelper
100
+ include SystemTesting::TestHelpers::UndefMethods
101
+
102
+ def initialize(*) # :nodoc:
103
+ super
104
+ self.class.driver.use
105
+ end
106
+
107
+ def self.start_application # :nodoc:
108
+ Capybara.app = Rack::Builder.new do
109
+ map "/" do
110
+ run Rails.application
111
+ end
112
+ end
113
+
114
+ SystemTesting::Server.new.run
115
+ end
116
+
117
+ class_attribute :driver, instance_accessor: false
118
+
119
+ # System Test configuration options
120
+ #
121
+ # The default settings are Selenium, using Chrome, with a screen size
122
+ # of 1400x1400.
123
+ #
124
+ # Examples:
125
+ #
126
+ # driven_by :poltergeist
127
+ #
128
+ # driven_by :selenium, screen_size: [800, 800]
129
+ #
130
+ # driven_by :selenium, using: :chrome
131
+ #
132
+ # driven_by :selenium, using: :headless_chrome
133
+ #
134
+ # driven_by :selenium, using: :firefox
135
+ #
136
+ # driven_by :selenium, using: :headless_firefox
137
+ def self.driven_by(driver, using: :chrome, screen_size: [1400, 1400], options: {})
138
+ self.driver = SystemTesting::Driver.new(driver, using: using, screen_size: screen_size, options: options)
139
+ end
140
+
141
+ driven_by :selenium
142
+
143
+ ActiveSupport.run_load_hooks(:action_dispatch_system_test_case, self)
144
+ end
145
+
146
+ SystemTestCase.start_application
147
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionDispatch
4
+ module SystemTesting
5
+ class Browser # :nodoc:
6
+ attr_reader :name
7
+
8
+ def initialize(name)
9
+ @name = name
10
+ end
11
+
12
+ def type
13
+ case name
14
+ when :headless_chrome
15
+ :chrome
16
+ when :headless_firefox
17
+ :firefox
18
+ else
19
+ name
20
+ end
21
+ end
22
+
23
+ def options
24
+ case name
25
+ when :headless_chrome
26
+ headless_chrome_browser_options
27
+ when :headless_firefox
28
+ headless_firefox_browser_options
29
+ end
30
+ end
31
+
32
+ private
33
+ def headless_chrome_browser_options
34
+ options = Selenium::WebDriver::Chrome::Options.new
35
+ options.args << "--headless"
36
+ options.args << "--disable-gpu" if Gem.win_platform?
37
+
38
+ options
39
+ end
40
+
41
+ def headless_firefox_browser_options
42
+ options = Selenium::WebDriver::Firefox::Options.new
43
+ options.args << "-headless"
44
+
45
+ options
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionDispatch
4
+ module SystemTesting
5
+ class Driver # :nodoc:
6
+ def initialize(name, **options)
7
+ @name = name
8
+ @browser = Browser.new(options[:using])
9
+ @screen_size = options[:screen_size]
10
+ @options = options[:options]
11
+ end
12
+
13
+ def use
14
+ register if registerable?
15
+
16
+ setup
17
+ end
18
+
19
+ private
20
+ def registerable?
21
+ [:selenium, :poltergeist, :webkit].include?(@name)
22
+ end
23
+
24
+ def register
25
+ Capybara.register_driver @name do |app|
26
+ case @name
27
+ when :selenium then register_selenium(app)
28
+ when :poltergeist then register_poltergeist(app)
29
+ when :webkit then register_webkit(app)
30
+ end
31
+ end
32
+ end
33
+
34
+ def browser_options
35
+ @options.merge(options: @browser.options).compact
36
+ end
37
+
38
+ def register_selenium(app)
39
+ Capybara::Selenium::Driver.new(app, { browser: @browser.type }.merge(browser_options)).tap do |driver|
40
+ driver.browser.manage.window.size = Selenium::WebDriver::Dimension.new(*@screen_size)
41
+ end
42
+ end
43
+
44
+ def register_poltergeist(app)
45
+ Capybara::Poltergeist::Driver.new(app, @options.merge(window_size: @screen_size))
46
+ end
47
+
48
+ def register_webkit(app)
49
+ Capybara::Webkit::Driver.new(app, Capybara::Webkit::Configuration.to_hash.merge(@options)).tap do |driver|
50
+ driver.resize_window_to(driver.current_window_handle, *@screen_size)
51
+ end
52
+ end
53
+
54
+ def setup
55
+ Capybara.current_driver = @name
56
+ end
57
+ end
58
+ end
59
+ end