capybara 2.15.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (177) hide show
  1. checksums.yaml +5 -5
  2. data/History.md +137 -2
  3. data/README.md +36 -25
  4. data/lib/capybara/config.rb +11 -57
  5. data/lib/capybara/cucumber.rb +2 -3
  6. data/lib/capybara/driver/base.rb +19 -16
  7. data/lib/capybara/driver/node.rb +5 -4
  8. data/lib/capybara/dsl.rb +1 -0
  9. data/lib/capybara/helpers.rb +19 -29
  10. data/lib/capybara/minitest/spec.rb +16 -13
  11. data/lib/capybara/minitest.rb +140 -137
  12. data/lib/capybara/node/actions.rb +68 -89
  13. data/lib/capybara/node/base.rb +11 -18
  14. data/lib/capybara/node/document.rb +2 -2
  15. data/lib/capybara/node/document_matchers.rb +8 -8
  16. data/lib/capybara/node/element.rb +32 -42
  17. data/lib/capybara/node/finders.rb +64 -71
  18. data/lib/capybara/node/matchers.rb +50 -71
  19. data/lib/capybara/node/simple.rb +11 -17
  20. data/lib/capybara/queries/ancestor_query.rb +12 -8
  21. data/lib/capybara/queries/base_query.rb +22 -18
  22. data/lib/capybara/queries/current_path_query.rb +12 -25
  23. data/lib/capybara/queries/match_query.rb +3 -7
  24. data/lib/capybara/queries/selector_query.rb +100 -96
  25. data/lib/capybara/queries/sibling_query.rb +5 -5
  26. data/lib/capybara/queries/text_query.rb +35 -35
  27. data/lib/capybara/queries/title_query.rb +8 -11
  28. data/lib/capybara/rack_test/browser.rb +15 -18
  29. data/lib/capybara/rack_test/css_handlers.rb +6 -4
  30. data/lib/capybara/rack_test/driver.rb +6 -10
  31. data/lib/capybara/rack_test/form.rb +52 -39
  32. data/lib/capybara/rack_test/node.rb +93 -63
  33. data/lib/capybara/rails.rb +2 -6
  34. data/lib/capybara/result.rb +22 -22
  35. data/lib/capybara/rspec/compound.rb +5 -10
  36. data/lib/capybara/rspec/features.rb +17 -48
  37. data/lib/capybara/rspec/matcher_proxies.rb +31 -15
  38. data/lib/capybara/rspec/matchers.rb +116 -58
  39. data/lib/capybara/rspec.rb +5 -10
  40. data/lib/capybara/selector/css.rb +6 -11
  41. data/lib/capybara/selector/filter.rb +1 -17
  42. data/lib/capybara/selector/filter_set.rb +18 -15
  43. data/lib/capybara/selector/filters/base.rb +7 -6
  44. data/lib/capybara/selector/filters/expression_filter.rb +6 -23
  45. data/lib/capybara/selector/filters/node_filter.rb +2 -12
  46. data/lib/capybara/selector/selector.rb +28 -34
  47. data/lib/capybara/selector.rb +129 -117
  48. data/lib/capybara/selenium/driver.rb +172 -163
  49. data/lib/capybara/selenium/node.rb +218 -104
  50. data/lib/capybara/server.rb +3 -2
  51. data/lib/capybara/session/config.rb +47 -59
  52. data/lib/capybara/session/matchers.rb +23 -14
  53. data/lib/capybara/session.rb +175 -229
  54. data/lib/capybara/spec/fixtures/no_extension +1 -0
  55. data/lib/capybara/spec/public/test.js +38 -6
  56. data/lib/capybara/spec/session/accept_alert_spec.rb +1 -0
  57. data/lib/capybara/spec/session/accept_confirm_spec.rb +3 -2
  58. data/lib/capybara/spec/session/accept_prompt_spec.rb +30 -1
  59. data/lib/capybara/spec/session/all_spec.rb +31 -18
  60. data/lib/capybara/spec/session/ancestor_spec.rb +6 -8
  61. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +6 -5
  62. data/lib/capybara/spec/session/assert_current_path.rb +12 -11
  63. data/lib/capybara/spec/session/assert_selector.rb +1 -0
  64. data/lib/capybara/spec/session/assert_text.rb +31 -23
  65. data/lib/capybara/spec/session/assert_title.rb +13 -3
  66. data/lib/capybara/spec/session/attach_file_spec.rb +57 -29
  67. data/lib/capybara/spec/session/body_spec.rb +1 -0
  68. data/lib/capybara/spec/session/check_spec.rb +7 -6
  69. data/lib/capybara/spec/session/choose_spec.rb +5 -4
  70. data/lib/capybara/spec/session/click_button_spec.rb +24 -32
  71. data/lib/capybara/spec/session/click_link_or_button_spec.rb +8 -7
  72. data/lib/capybara/spec/session/click_link_spec.rb +8 -7
  73. data/lib/capybara/spec/session/current_scope_spec.rb +4 -3
  74. data/lib/capybara/spec/session/current_url_spec.rb +19 -8
  75. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +1 -1
  76. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +1 -0
  77. data/lib/capybara/spec/session/element/assert_match_selector.rb +1 -1
  78. data/lib/capybara/spec/session/element/match_xpath_spec.rb +1 -1
  79. data/lib/capybara/spec/session/element/matches_selector_spec.rb +5 -5
  80. data/lib/capybara/spec/session/evaluate_async_script_spec.rb +23 -0
  81. data/lib/capybara/spec/session/evaluate_script_spec.rb +5 -4
  82. data/lib/capybara/spec/session/execute_script_spec.rb +4 -3
  83. data/lib/capybara/spec/session/fill_in_spec.rb +30 -5
  84. data/lib/capybara/spec/session/find_button_spec.rb +4 -3
  85. data/lib/capybara/spec/session/find_by_id_spec.rb +2 -1
  86. data/lib/capybara/spec/session/find_field_spec.rb +9 -15
  87. data/lib/capybara/spec/session/find_link_spec.rb +6 -5
  88. data/lib/capybara/spec/session/find_spec.rb +37 -31
  89. data/lib/capybara/spec/session/first_spec.rb +60 -33
  90. data/lib/capybara/spec/session/frame/frame_title_spec.rb +23 -0
  91. data/lib/capybara/spec/session/frame/frame_url_spec.rb +23 -0
  92. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +2 -1
  93. data/lib/capybara/spec/session/frame/within_frame_spec.rb +9 -16
  94. data/lib/capybara/spec/session/go_back_spec.rb +1 -0
  95. data/lib/capybara/spec/session/go_forward_spec.rb +1 -0
  96. data/lib/capybara/spec/session/has_all_selectors_spec.rb +69 -0
  97. data/lib/capybara/spec/session/has_button_spec.rb +2 -1
  98. data/lib/capybara/spec/session/has_css_spec.rb +3 -2
  99. data/lib/capybara/spec/session/has_current_path_spec.rb +49 -22
  100. data/lib/capybara/spec/session/has_field_spec.rb +4 -3
  101. data/lib/capybara/spec/session/has_link_spec.rb +5 -4
  102. data/lib/capybara/spec/session/has_none_selectors_spec.rb +76 -0
  103. data/lib/capybara/spec/session/has_select_spec.rb +32 -31
  104. data/lib/capybara/spec/session/has_selector_spec.rb +5 -4
  105. data/lib/capybara/spec/session/has_table_spec.rb +2 -1
  106. data/lib/capybara/spec/session/has_text_spec.rb +9 -13
  107. data/lib/capybara/spec/session/has_title_spec.rb +1 -0
  108. data/lib/capybara/spec/session/has_xpath_spec.rb +1 -0
  109. data/lib/capybara/spec/session/headers.rb +2 -1
  110. data/lib/capybara/spec/session/html_spec.rb +1 -0
  111. data/lib/capybara/spec/session/node_spec.rb +107 -58
  112. data/lib/capybara/spec/session/node_wrapper_spec.rb +36 -0
  113. data/lib/capybara/spec/session/refresh_spec.rb +6 -2
  114. data/lib/capybara/spec/session/reset_session_spec.rb +19 -0
  115. data/lib/capybara/spec/session/response_code.rb +1 -0
  116. data/lib/capybara/spec/session/save_and_open_page_spec.rb +1 -0
  117. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +6 -11
  118. data/lib/capybara/spec/session/save_page_spec.rb +1 -17
  119. data/lib/capybara/spec/session/save_screenshot_spec.rb +3 -3
  120. data/lib/capybara/spec/session/select_spec.rb +21 -20
  121. data/lib/capybara/spec/session/selectors_spec.rb +2 -2
  122. data/lib/capybara/spec/session/sibling_spec.rb +1 -1
  123. data/lib/capybara/spec/session/text_spec.rb +17 -3
  124. data/lib/capybara/spec/session/title_spec.rb +11 -1
  125. data/lib/capybara/spec/session/uncheck_spec.rb +4 -3
  126. data/lib/capybara/spec/session/unselect_spec.rb +7 -6
  127. data/lib/capybara/spec/session/visit_spec.rb +64 -3
  128. data/lib/capybara/spec/session/window/become_closed_spec.rb +2 -1
  129. data/lib/capybara/spec/session/window/current_window_spec.rb +1 -0
  130. data/lib/capybara/spec/session/window/open_new_window_spec.rb +1 -0
  131. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +2 -1
  132. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +2 -1
  133. data/lib/capybara/spec/session/window/window_spec.rb +12 -12
  134. data/lib/capybara/spec/session/window/windows_spec.rb +2 -3
  135. data/lib/capybara/spec/session/window/within_window_spec.rb +15 -71
  136. data/lib/capybara/spec/session/within_spec.rb +1 -0
  137. data/lib/capybara/spec/spec_helper.rb +36 -18
  138. data/lib/capybara/spec/test_app.rb +17 -9
  139. data/lib/capybara/spec/views/form.erb +7 -0
  140. data/lib/capybara/spec/views/initial_alert.erb +10 -0
  141. data/lib/capybara/spec/views/with_fixed_header_footer.erb +17 -0
  142. data/lib/capybara/spec/views/with_hover.erb +5 -0
  143. data/lib/capybara/spec/views/with_html.erb +27 -1
  144. data/lib/capybara/spec/views/with_js.erb +11 -0
  145. data/lib/capybara/spec/views/within_frames.erb +4 -1
  146. data/lib/capybara/version.rb +2 -1
  147. data/lib/capybara/window.rb +6 -10
  148. data/lib/capybara.rb +29 -26
  149. data/spec/basic_node_spec.rb +1 -0
  150. data/spec/capybara_spec.rb +16 -69
  151. data/spec/dsl_spec.rb +5 -13
  152. data/spec/filter_set_spec.rb +5 -4
  153. data/spec/fixtures/selenium_driver_rspec_failure.rb +2 -1
  154. data/spec/fixtures/selenium_driver_rspec_success.rb +3 -2
  155. data/spec/minitest_spec.rb +13 -4
  156. data/spec/minitest_spec_spec.rb +12 -3
  157. data/spec/per_session_config_spec.rb +9 -8
  158. data/spec/rack_test_spec.rb +21 -20
  159. data/spec/result_spec.rb +17 -16
  160. data/spec/rspec/features_spec.rb +17 -14
  161. data/spec/rspec/scenarios_spec.rb +5 -7
  162. data/spec/rspec/shared_spec_matchers.rb +96 -99
  163. data/spec/rspec/views_spec.rb +2 -1
  164. data/spec/rspec_matchers_spec.rb +18 -2
  165. data/spec/rspec_spec.rb +11 -15
  166. data/spec/selector_spec.rb +5 -6
  167. data/spec/selenium_spec_chrome.rb +20 -11
  168. data/spec/selenium_spec_edge.rb +27 -0
  169. data/spec/selenium_spec_ie.rb +31 -0
  170. data/spec/selenium_spec_marionette.rb +38 -12
  171. data/spec/server_spec.rb +33 -33
  172. data/spec/session_spec.rb +2 -1
  173. data/spec/shared_selenium_session.rb +82 -22
  174. data/spec/spec_helper.rb +3 -6
  175. metadata +76 -81
  176. data/lib/capybara/query.rb +0 -7
  177. data/spec/selenium_spec_firefox.rb +0 -68
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'capybara/session/matchers'
3
4
  require 'addressable/uri'
4
5
 
5
6
  module Capybara
6
-
7
7
  ##
8
8
  #
9
9
  # The Session class represents a single user's interaction with the system. The Session can use
@@ -38,64 +38,63 @@ module Capybara
38
38
  class Session
39
39
  include Capybara::SessionMatchers
40
40
 
41
- NODE_METHODS = [
42
- :all, :first, :attach_file, :text, :check, :choose,
43
- :click_link_or_button, :click_button, :click_link, :field_labeled,
44
- :fill_in, :find, :find_all, :find_button, :find_by_id, :find_field, :find_link,
45
- :has_content?, :has_text?, :has_css?, :has_no_content?, :has_no_text?,
46
- :has_no_css?, :has_no_xpath?, :resolve, :has_xpath?, :select, :uncheck,
47
- :has_link?, :has_no_link?, :has_button?, :has_no_button?, :has_field?,
48
- :has_no_field?, :has_checked_field?, :has_unchecked_field?,
49
- :has_no_table?, :has_table?, :unselect, :has_select?, :has_no_select?,
50
- :has_selector?, :has_no_selector?, :click_on, :has_no_checked_field?,
51
- :has_no_unchecked_field?, :query, :assert_selector, :assert_no_selector,
52
- :assert_all_of_selectors, :assert_none_of_selectors,
53
- :refute_selector, :assert_text, :assert_no_text
54
- ]
41
+ NODE_METHODS = %i[
42
+ all first attach_file text check choose
43
+ click_link_or_button click_button click_link
44
+ fill_in find find_all find_button find_by_id find_field find_link
45
+ has_content? has_text? has_css? has_no_content? has_no_text?
46
+ has_no_css? has_no_xpath? resolve has_xpath? select uncheck
47
+ has_link? has_no_link? has_button? has_no_button? has_field?
48
+ has_no_field? has_checked_field? has_unchecked_field?
49
+ has_no_table? has_table? unselect has_select? has_no_select?
50
+ has_selector? has_no_selector? click_on has_no_checked_field?
51
+ has_no_unchecked_field? query assert_selector assert_no_selector
52
+ assert_all_of_selectors assert_none_of_selectors
53
+ refute_selector assert_text assert_no_text
54
+ ].freeze
55
55
  # @api private
56
- DOCUMENT_METHODS = [
57
- :title, :assert_title, :assert_no_title, :has_title?, :has_no_title?
58
- ]
59
- SESSION_METHODS = [
60
- :body, :html, :source, :current_url, :current_host, :current_path,
61
- :execute_script, :evaluate_script, :visit, :refresh, :go_back, :go_forward,
62
- :within, :within_element, :within_fieldset, :within_table, :within_frame, :switch_to_frame,
63
- :current_window, :windows, :open_new_window, :switch_to_window, :within_window, :window_opened_by,
64
- :save_page, :save_and_open_page, :save_screenshot,
65
- :save_and_open_screenshot, :reset_session!, :response_headers,
66
- :status_code, :current_scope,
67
- :assert_current_path, :assert_no_current_path, :has_current_path?, :has_no_current_path?
68
- ] + DOCUMENT_METHODS
69
- MODAL_METHODS = [
70
- :accept_alert, :accept_confirm, :dismiss_confirm, :accept_prompt,
71
- :dismiss_prompt
72
- ]
56
+ DOCUMENT_METHODS = %i[
57
+ title assert_title assert_no_title has_title? has_no_title?
58
+ ].freeze
59
+ SESSION_METHODS = %i[
60
+ body html source current_url current_host current_path
61
+ execute_script evaluate_script visit refresh go_back go_forward
62
+ within within_element within_fieldset within_table within_frame switch_to_frame
63
+ current_window windows open_new_window switch_to_window within_window window_opened_by
64
+ save_page save_and_open_page save_screenshot
65
+ save_and_open_screenshot reset_session! response_headers
66
+ status_code current_scope
67
+ assert_current_path assert_no_current_path has_current_path? has_no_current_path?
68
+ ].freeze + DOCUMENT_METHODS
69
+ MODAL_METHODS = %i[
70
+ accept_alert accept_confirm dismiss_confirm accept_prompt dismiss_prompt
71
+ ].freeze
73
72
  DSL_METHODS = NODE_METHODS + SESSION_METHODS + MODAL_METHODS
74
73
 
75
74
  attr_reader :mode, :app, :server
76
75
  attr_accessor :synchronized
77
76
 
78
- def initialize(mode, app=nil)
77
+ def initialize(mode, app = nil)
79
78
  raise TypeError, "The second parameter to Session::new should be a rack app if passed." if app && !app.respond_to?(:call)
80
79
  @@instance_created = true
81
80
  @mode = mode
82
81
  @app = app
83
82
  if block_given?
84
83
  raise "A configuration block is only accepted when Capybara.threadsafe == true" unless Capybara.threadsafe
85
- yield config if block_given?
84
+ yield config
86
85
  end
87
- if config.run_server and @app and driver.needs_server?
88
- @server = Capybara::Server.new(@app, config.server_port, config.server_host, config.server_errors).boot
86
+ @server = if config.run_server and @app and driver.needs_server?
87
+ Capybara::Server.new(@app, config.server_port, config.server_host, config.server_errors).boot
89
88
  else
90
- @server = nil
89
+ nil
91
90
  end
92
91
  @touched = false
93
92
  end
94
93
 
95
94
  def driver
96
95
  @driver ||= begin
97
- unless Capybara.drivers.has_key?(mode)
98
- other_drivers = Capybara.drivers.keys.map { |key| key.inspect }
96
+ unless Capybara.drivers.key?(mode)
97
+ other_drivers = Capybara.drivers.keys.map(&:inspect)
99
98
  raise Capybara::DriverNotFoundError, "no driver called #{mode.inspect} was found, available drivers: #{other_drivers.join(', ')}"
100
99
  end
101
100
  driver = Capybara.drivers[mode].call(app)
@@ -145,7 +144,7 @@ module Capybara
145
144
  raise CapybaraError, "Your application server raised an error - It has been raised in your test code because Capybara.raise_server_errors == true"
146
145
  end
147
146
  rescue CapybaraError
148
- #needed to get the cause set correctly in JRuby -- otherwise we could just do raise @server.error
147
+ # needed to get the cause set correctly in JRuby -- otherwise we could just do raise @server.error
149
148
  raise @server.error, @server.error.message, @server.error.backtrace
150
149
  ensure
151
150
  @server.reset_error!
@@ -196,8 +195,9 @@ module Capybara
196
195
 
197
196
  # Addressable doesn't support opaque URIs - we want nil here
198
197
  return nil if uri.scheme == "about"
198
+
199
199
  path = uri.path
200
- path if path and not path.empty?
200
+ path if path && !path.empty?
201
201
  end
202
202
 
203
203
  ##
@@ -247,24 +247,29 @@ module Capybara
247
247
  raise_server_error!
248
248
  @touched = true
249
249
 
250
- visit_uri = URI.parse(visit_uri.to_s)
250
+ visit_uri = ::Addressable::URI.parse(visit_uri.to_s)
251
251
 
252
252
  uri_base = if @server
253
- visit_uri.port = @server.port if config.always_include_port && (visit_uri.port == visit_uri.default_port)
254
- URI.parse(config.app_host || "http://#{@server.host}:#{@server.port}")
253
+ ::Addressable::URI.parse(config.app_host || "http://#{@server.host}:#{@server.port}")
255
254
  else
256
- config.app_host && URI.parse(config.app_host)
255
+ config.app_host && ::Addressable::URI.parse(config.app_host)
257
256
  end
258
257
 
259
- # TODO - this is only for compatability with previous 2.x behavior that concatenated
260
- # Capybara.app_host and a "relative" path - Consider removing in 3.0
261
- # @abotalov brought up a good point about this behavior potentially being useful to people
262
- # deploying to a subdirectory and/or single page apps where only the url fragment changes
263
- if visit_uri.scheme.nil? && uri_base
264
- visit_uri.path = uri_base.path + visit_uri.path
265
- end
258
+ if uri_base && [nil, 'http', 'https'].include?(visit_uri.scheme)
259
+ if visit_uri.relative?
260
+ uri_base.port ||= @server.port if @server && config.always_include_port
261
+
262
+ visit_uri_parts = visit_uri.to_hash.delete_if { |_k, v| v.nil? }
266
263
 
267
- visit_uri = uri_base.merge(visit_uri) unless uri_base.nil?
264
+ # Useful to people deploying to a subdirectory
265
+ # and/or single page apps where only the url fragment changes
266
+ visit_uri_parts[:path] = uri_base.path + visit_uri.path
267
+
268
+ visit_uri = uri_base.merge(visit_uri_parts)
269
+ elsif @server && config.always_include_port
270
+ visit_uri.port ||= @server.port
271
+ end
272
+ end
268
273
 
269
274
  driver.visit(visit_uri.to_s)
270
275
  end
@@ -330,7 +335,7 @@ module Capybara
330
335
  # @raise [Capybara::ElementNotFound] If the scope can't be found before time expires
331
336
  #
332
337
  def within(*args)
333
- new_scope = if args.first.is_a?(Capybara::Node::Base) then args.first else find(*args) end
338
+ new_scope = args.first.respond_to?(:to_capybara_node) ? args.first.to_capybara_node : find(*args)
334
339
  begin
335
340
  scopes.push(new_scope)
336
341
  yield
@@ -347,9 +352,7 @@ module Capybara
347
352
  # @param [String] locator Id or legend of the fieldset
348
353
  #
349
354
  def within_fieldset(locator)
350
- within :fieldset, locator do
351
- yield
352
- end
355
+ within(:fieldset, locator) { yield }
353
356
  end
354
357
 
355
358
  ##
@@ -359,9 +362,7 @@ module Capybara
359
362
  # @param [String] locator Id or caption of the table
360
363
  #
361
364
  def within_table(locator)
362
- within :table, locator do
363
- yield
364
- end
365
+ within(:table, locator) { yield }
365
366
  end
366
367
 
367
368
  ##
@@ -385,15 +386,19 @@ module Capybara
385
386
  driver.switch_to_frame(frame)
386
387
  scopes.push(:frame)
387
388
  when :parent
388
- raise Capybara::ScopeError, "`switch_to_frame(:parent)` cannot be called from inside a descendant frame's "\
389
- "`within` block." if scopes.last() != :frame
389
+ if scopes.last != :frame
390
+ raise Capybara::ScopeError, "`switch_to_frame(:parent)` cannot be called from inside a descendant frame's "\
391
+ "`within` block."
392
+ end
390
393
  scopes.pop
391
394
  driver.switch_to_frame(:parent)
392
395
  when :top
393
396
  idx = scopes.index(:frame)
394
397
  if idx
395
- raise Capybara::ScopeError, "`switch_to_frame(:top)` cannot be called from inside a descendant frame's "\
396
- "`within` block." if scopes.slice(idx..-1).any? {|scope| ![:frame, nil].include?(scope)}
398
+ if scopes.slice(idx..-1).any? { |scope| ![:frame, nil].include?(scope) }
399
+ raise Capybara::ScopeError, "`switch_to_frame(:top)` cannot be called from inside a descendant frame's "\
400
+ "`within` block."
401
+ end
397
402
  scopes.slice!(idx..-1)
398
403
  driver.switch_to_frame(:top)
399
404
  end
@@ -408,34 +413,16 @@ module Capybara
408
413
  # @overload within_frame(element)
409
414
  # @param [Capybara::Node::Element] frame element
410
415
  # @overload within_frame([kind = :frame], locator, options = {})
411
- # @param [Symobl] kind Optional selector type (:css, :xpath, :field, etc.) - Defaults to :frame
416
+ # @param [Symbol] kind Optional selector type (:css, :xpath, :field, etc.) - Defaults to :frame
412
417
  # @param [String] locator The locator for the given selector kind. For :frame this is the name/id of a frame/iframe element
413
418
  # @overload within_frame(index)
414
419
  # @param [Integer] index index of a frame (0 based)
415
420
  def within_frame(*args)
416
- frame = _find_frame(*args)
417
-
421
+ switch_to_frame(_find_frame(*args))
418
422
  begin
419
- switch_to_frame(frame)
420
- begin
421
- yield
422
- ensure
423
- switch_to_frame(:parent)
424
- end
425
- rescue Capybara::NotSupportedByDriverError
426
- # Support older driver frame API for now
427
- if driver.respond_to?(:within_frame)
428
- begin
429
- scopes.push(:frame)
430
- driver.within_frame(frame) do
431
- yield
432
- end
433
- ensure
434
- scopes.pop
435
- end
436
- else
437
- raise
438
- end
423
+ yield
424
+ ensure
425
+ switch_to_frame(:parent)
439
426
  end
440
427
  end
441
428
 
@@ -481,22 +468,18 @@ module Capybara
481
468
  # @raise [Capybara::WindowError] if no window matches given block
482
469
  # @overload switch_to_window(window)
483
470
  # @param window [Capybara::Window] window that should be switched to
484
- # @raise [Capybara::Driver::Base#no_such_window_error] if non-existent (e.g. closed) window was passed
471
+ # @raise [Capybara::Driver::Base#no_such_window_error] if nonexistent (e.g. closed) window was passed
485
472
  #
486
473
  # @return [Capybara::Window] window that has been switched to
487
474
  # @raise [Capybara::ScopeError] if this method is invoked inside `within` or
488
475
  # `within_frame` methods
489
476
  # @raise [ArgumentError] if both or neither arguments were provided
490
477
  #
491
- def switch_to_window(window = nil, options= {}, &window_locator)
492
- options, window = window, nil if window.is_a? Hash
493
-
478
+ def switch_to_window(window = nil, **options, &window_locator)
494
479
  block_given = block_given?
495
- if window && block_given
496
- raise ArgumentError, "`switch_to_window` can take either a block or a window, not both"
497
- elsif !window && !block_given
498
- raise ArgumentError, "`switch_to_window`: either window or block should be provided"
499
- elsif !scopes.last.nil?
480
+ raise ArgumentError, "`switch_to_window` can take either a block or a window, not both" if window && block_given
481
+ raise ArgumentError, "`switch_to_window`: either window or block should be provided" if !window && !block_given
482
+ unless scopes.last.nil?
500
483
  raise Capybara::ScopeError, "`switch_to_window` is not supposed to be invoked from "\
501
484
  "`within` or `within_frame` blocks."
502
485
  end
@@ -514,58 +497,37 @@ module Capybara
514
497
  # @overload within_window(window) { do_something }
515
498
  # @param window [Capybara::Window] instance of `Capybara::Window` class
516
499
  # that will be switched to
517
- # @raise [driver#no_such_window_error] if unexistent (e.g. closed) window was passed
500
+ # @raise [driver#no_such_window_error] if nonexistent (e.g. closed) window was passed
518
501
  # @overload within_window(proc_or_lambda) { do_something }
519
502
  # @param lambda [Proc] lambda. First window for which lambda
520
503
  # returns a value other than false or nil will be switched to.
521
504
  # @example
522
505
  # within_window(->{ page.title == 'Page title' }) { click_button 'Submit' }
523
506
  # @raise [Capybara::WindowError] if no window matching lambda was found
524
- # @overload within_window(string) { do_something }
525
- # @deprecated Pass window or lambda instead
526
- # @param [String] handle, name, url or title of the window
527
507
  #
528
508
  # @raise [Capybara::ScopeError] if this method is invoked inside `within_frame` method
529
509
  # @return value returned by the block
530
510
  #
531
- def within_window(window_or_handle)
532
- if window_or_handle.instance_of?(Capybara::Window)
533
- original = current_window
534
- scopes << nil
535
- begin
536
- _switch_to_window(window_or_handle) unless original == window_or_handle
537
- begin
538
- yield
539
- ensure
540
- _switch_to_window(original) unless original == window_or_handle
541
- end
542
- ensure
543
- scopes.pop
544
- end
545
- elsif window_or_handle.is_a?(Proc)
546
- original = current_window
547
- scopes << nil
548
- begin
549
- _switch_to_window { window_or_handle.call }
550
- begin
551
- yield
552
- ensure
553
- _switch_to_window(original)
554
- end
555
- ensure
556
- scopes.pop
511
+ def within_window(window_or_proc)
512
+ original = current_window
513
+ scopes << nil
514
+ begin
515
+ case window_or_proc
516
+ when Capybara::Window
517
+ _switch_to_window(window_or_proc) unless original == window_or_proc
518
+ when Proc
519
+ _switch_to_window { window_or_proc.call }
520
+ else
521
+ raise ArgumentError("`#within_window` requires a `Capybara::Window` instance or a lambda")
557
522
  end
558
- else
559
- offending_line = caller.first
560
- file_line = offending_line.match(/^(.+?):(\d+)/)[0]
561
- warn "DEPRECATION WARNING: Passing string argument to #within_window is deprecated. "\
562
- "Pass window object or lambda. (called from #{file_line})"
523
+
563
524
  begin
564
- scopes << nil
565
- driver.within_window(window_or_handle) { yield }
525
+ yield
566
526
  ensure
567
- scopes.pop
527
+ _switch_to_window(original) unless original == window_or_proc
568
528
  end
529
+ ensure
530
+ scopes.pop
569
531
  end
570
532
  end
571
533
 
@@ -575,15 +537,16 @@ module Capybara
575
537
  # It's better to use this method than `windows.last`
576
538
  # {https://dvcs.w3.org/hg/webdriver/raw-file/default/webdriver-spec.html#h_note_10 as order of windows isn't defined in some drivers}
577
539
  #
578
- # @param options [Hash]
579
- # @option options [Numeric] :wait (Capybara.default_max_wait_time) maximum wait time
580
- # @return [Capybara::Window] the window that has been opened within a block
581
- # @raise [Capybara::WindowError] if block passed to window hasn't opened window
582
- # or opened more than one window
540
+ # @overload window_opened_by(**options, &block)
541
+ # @param options [Hash]
542
+ # @option options [Numeric] :wait (Capybara.default_max_wait_time) maximum wait time
543
+ # @return [Capybara::Window] the window that has been opened within a block
544
+ # @raise [Capybara::WindowError] if block passed to window hasn't opened window
545
+ # or opened more than one window
583
546
  #
584
- def window_opened_by(options = {}, &block)
547
+ def window_opened_by(**options)
585
548
  old_handles = driver.window_handles
586
- block.call
549
+ yield
587
550
 
588
551
  wait_time = Capybara::Queries::BaseQuery.wait(options, config.default_max_wait_time)
589
552
  document.synchronize(wait_time, errors: [Capybara::WindowError]) do
@@ -607,12 +570,7 @@ module Capybara
607
570
  #
608
571
  def execute_script(script, *args)
609
572
  @touched = true
610
- if args.empty?
611
- driver.execute_script(script)
612
- else
613
- raise Capybara::NotSupportedByDriverError, "The current driver does not support execute_script arguments" if driver.method(:execute_script).arity == 1
614
- driver.execute_script(script, *args.map { |arg| arg.is_a?(Capybara::Node::Element) ? arg.base : arg} )
615
- end
573
+ driver.execute_script(script, *args.map { |arg| arg.is_a?(Capybara::Node::Element) ? arg.base : arg })
616
574
  end
617
575
 
618
576
  ##
@@ -626,12 +584,20 @@ module Capybara
626
584
  #
627
585
  def evaluate_script(script, *args)
628
586
  @touched = true
629
- result = if args.empty?
630
- driver.evaluate_script(script)
631
- else
632
- raise Capybara::NotSupportedByDriverError, "The current driver does not support evaluate_script arguments" if driver.method(:evaluate_script).arity == 1
633
- driver.evaluate_script(script, *args.map { |arg| arg.is_a?(Capybara::Node::Element) ? arg.base : arg} )
634
- end
587
+ result = driver.evaluate_script(script, *args.map { |arg| arg.is_a?(Capybara::Node::Element) ? arg.base : arg })
588
+ element_script_result(result)
589
+ end
590
+
591
+ ##
592
+ #
593
+ # Evaluate the given JavaScript and obtain the result from a callback function which will be passed as the last argument to the script.
594
+ #
595
+ # @param [String] script A string of JavaScript to evaluate
596
+ # @return [Object] The result of the evaluated JavaScript (may be driver specific)
597
+ #
598
+ def evaluate_async_script(script, *args)
599
+ @touched = true
600
+ result = driver.evaluate_async_script(script, *args.map { |arg| arg.is_a?(Capybara::Node::Element) ? arg.base : arg })
635
601
  element_script_result(result)
636
602
  end
637
603
 
@@ -640,15 +606,23 @@ module Capybara
640
606
  # Execute the block, accepting a alert.
641
607
  #
642
608
  # @!macro modal_params
609
+ # Expects a block whose actions will trigger the display modal to appear
610
+ # @example
611
+ # $0 do
612
+ # click_link('link that triggers appearance of system modal')
613
+ # end
643
614
  # @overload $0(text, options = {}, &blk)
644
615
  # @param text [String, Regexp] Text or regex to match against the text in the modal. If not provided any modal is matched
616
+ # @option options [Numeric] :wait (Capybara.default_max_wait_time) Maximum time to wait for the modal to appear after executing the block.
617
+ # @yield Block whose actions will trigger the system modal
645
618
  # @overload $0(options = {}, &blk)
646
- # @option options [Numeric] :wait (Capybara.default_max_wait_time) Maximum time to wait for the modal to appear after executing the block.
619
+ # @option options [Numeric] :wait (Capybara.default_max_wait_time) Maximum time to wait for the modal to appear after executing the block.
620
+ # @yield Block whose actions will trigger the system modal
647
621
  # @return [String] the message shown in the modal
648
622
  # @raise [Capybara::ModalNotFound] if modal dialog hasn't been found
649
623
  #
650
- def accept_alert(text_or_options=nil, options={}, &blk)
651
- accept_modal(:alert, text_or_options, options, &blk)
624
+ def accept_alert(text = nil, **options, &blk)
625
+ accept_modal(:alert, text, options, &blk)
652
626
  end
653
627
 
654
628
  ##
@@ -657,8 +631,8 @@ module Capybara
657
631
  #
658
632
  # @macro modal_params
659
633
  #
660
- def accept_confirm(text_or_options=nil, options={}, &blk)
661
- accept_modal(:confirm, text_or_options, options, &blk)
634
+ def accept_confirm(text = nil, **options, &blk)
635
+ accept_modal(:confirm, text, options, &blk)
662
636
  end
663
637
 
664
638
  ##
@@ -667,8 +641,8 @@ module Capybara
667
641
  #
668
642
  # @macro modal_params
669
643
  #
670
- def dismiss_confirm(text_or_options=nil, options={}, &blk)
671
- dismiss_modal(:confirm, text_or_options, options, &blk)
644
+ def dismiss_confirm(text = nil, **options, &blk)
645
+ dismiss_modal(:confirm, text, options, &blk)
672
646
  end
673
647
 
674
648
  ##
@@ -678,8 +652,8 @@ module Capybara
678
652
  # @macro modal_params
679
653
  # @option options [String] :with Response to provide to the prompt
680
654
  #
681
- def accept_prompt(text_or_options=nil, options={}, &blk)
682
- accept_modal(:prompt, text_or_options, options, &blk)
655
+ def accept_prompt(text = nil, **options, &blk)
656
+ accept_modal(:prompt, text, options, &blk)
683
657
  end
684
658
 
685
659
  ##
@@ -688,8 +662,8 @@ module Capybara
688
662
  #
689
663
  # @macro modal_params
690
664
  #
691
- def dismiss_prompt(text_or_options=nil, options={}, &blk)
692
- dismiss_modal(:prompt, text_or_options, options, &blk)
665
+ def dismiss_prompt(text = nil, **options, &blk)
666
+ dismiss_modal(:prompt, text, options, &blk)
693
667
  end
694
668
 
695
669
  ##
@@ -699,17 +673,15 @@ module Capybara
699
673
  #
700
674
  # If invoked without arguments it will save file to `Capybara.save_path`
701
675
  # and file will be given randomly generated filename. If invoked with a relative path
702
- # the path will be relative to `Capybara.save_path`, which is different from
703
- # the previous behavior with `Capybara.save_and_open_page_path` where the relative path was
704
- # relative to Dir.pwd
676
+ # the path will be relative to `Capybara.save_path`
705
677
  #
706
678
  # @param [String] path the path to where it should be saved
707
679
  # @return [String] the path to which the file was saved
708
680
  #
709
681
  def save_page(path = nil)
710
- path = prepare_path(path, 'html')
711
- File.write(path, Capybara::Helpers.inject_asset_host(body, config.asset_host), mode: 'wb')
712
- path
682
+ prepare_path(path, 'html').tap do |p|
683
+ File.write(p, Capybara::Helpers.inject_asset_host(body, host: config.asset_host), mode: 'wb')
684
+ end
713
685
  end
714
686
 
715
687
  ##
@@ -718,15 +690,12 @@ module Capybara
718
690
  #
719
691
  # If invoked without arguments it will save file to `Capybara.save_path`
720
692
  # and file will be given randomly generated filename. If invoked with a relative path
721
- # the path will be relative to `Capybara.save_path`, which is different from
722
- # the previous behavior with `Capybara.save_and_open_page_path` where the relative path was
723
- # relative to Dir.pwd
693
+ # the path will be relative to `Capybara.save_path`
724
694
  #
725
695
  # @param [String] path the path to where it should be saved
726
696
  #
727
697
  def save_and_open_page(path = nil)
728
- path = save_page(path)
729
- open_file(path)
698
+ save_page(path).tap { |p| open_file(p) }
730
699
  end
731
700
 
732
701
  ##
@@ -735,17 +704,13 @@ module Capybara
735
704
  #
736
705
  # If invoked without arguments it will save file to `Capybara.save_path`
737
706
  # and file will be given randomly generated filename. If invoked with a relative path
738
- # the path will be relative to `Capybara.save_path`, which is different from
739
- # the previous behavior with `Capybara.save_and_open_page_path` where the relative path was
740
- # relative to Dir.pwd
707
+ # the path will be relative to `Capybara.save_path`
741
708
  #
742
709
  # @param [String] path the path to where it should be saved
743
710
  # @param [Hash] options a customizable set of options
744
711
  # @return [String] the path to which the file was saved
745
- def save_screenshot(path = nil, options = {})
746
- path = prepare_path(path, 'png')
747
- driver.save_screenshot(path, options)
748
- path
712
+ def save_screenshot(path = nil, **options)
713
+ prepare_path(path, 'png').tap { |p| driver.save_screenshot(p, options) }
749
714
  end
750
715
 
751
716
  ##
@@ -754,16 +719,15 @@ module Capybara
754
719
  #
755
720
  # If invoked without arguments it will save file to `Capybara.save_path`
756
721
  # and file will be given randomly generated filename. If invoked with a relative path
757
- # the path will be relative to `Capybara.save_path`, which is different from
758
- # the previous behavior with `Capybara.save_and_open_page_path` where the relative path was
759
- # relative to Dir.pwd
722
+ # the path will be relative to `Capybara.save_path`
760
723
  #
761
724
  # @param [String] path the path to where it should be saved
762
725
  # @param [Hash] options a customizable set of options
763
726
  #
764
- def save_and_open_screenshot(path = nil, options = {})
765
- path = save_screenshot(path, options)
766
- open_file(path)
727
+ def save_and_open_screenshot(path = nil, **options)
728
+ # rubocop:disable Lint/Debugger
729
+ save_screenshot(path, options).tap { |p| open_file(p) }
730
+ # rubocop:enable Lint/Debugger
767
731
  end
768
732
 
769
733
  def document
@@ -789,8 +753,7 @@ module Capybara
789
753
 
790
754
  def current_scope
791
755
  scope = scopes.last
792
- scope = document if [nil, :frame].include? scope
793
- scope
756
+ [nil, :frame].include?(scope) ? document : scope
794
757
  end
795
758
 
796
759
  ##
@@ -832,6 +795,7 @@ module Capybara
832
795
  Capybara::ReadOnlySessionConfig.new(Capybara.session_options)
833
796
  end
834
797
  end
798
+
835
799
  private
836
800
 
837
801
  @@instance_created = false
@@ -844,32 +808,21 @@ module Capybara
844
808
  driver.dismiss_modal(type, modal_options(text_or_options, options), &blk)
845
809
  end
846
810
 
847
- def modal_options(text_or_options, options)
848
- text_or_options, options = nil, text_or_options if text_or_options.is_a?(Hash)
849
- options[:text] ||= text_or_options unless text_or_options.nil?
811
+ def modal_options(text = nil, **options)
812
+ options[:text] ||= text unless text.nil?
850
813
  options[:wait] ||= config.default_max_wait_time
851
814
  options
852
815
  end
853
816
 
854
-
855
817
  def open_file(path)
856
- begin
857
- require "launchy"
858
- Launchy.open(path)
859
- rescue LoadError
860
- warn "File saved to #{path}."
861
- warn "Please install the launchy gem to open the file automatically."
862
- end
818
+ require "launchy"
819
+ Launchy.open(path)
820
+ rescue LoadError
821
+ warn "File saved to #{path}.\nPlease install the launchy gem to open the file automatically."
863
822
  end
864
823
 
865
824
  def prepare_path(path, extension)
866
- if config.save_path || config.save_and_open_page_path.nil?
867
- path = File.expand_path(path || default_fn(extension), config.save_path)
868
- else
869
- path = File.expand_path(default_fn(extension), config.save_and_open_page_path) if path.nil?
870
- end
871
- FileUtils.mkdir_p(File.dirname(path))
872
- path
825
+ File.expand_path(path || default_fn(extension), config.save_path).tap { |p| FileUtils.mkdir_p(File.dirname(p)) }
873
826
  end
874
827
 
875
828
  def default_fn(extension)
@@ -895,26 +848,22 @@ module Capybara
895
848
  end
896
849
 
897
850
  def _find_frame(*args)
898
- within(document) do # Previous 2.x versions ignored current scope when finding frames - consider changing in 3.0
899
- case args[0]
900
- when Capybara::Node::Element
901
- args[0]
902
- when String, Hash
903
- find(:frame, *args)
904
- when Symbol
905
- find(*args)
906
- when Integer
907
- idx = args[0]
908
- all(:frame, minimum: idx+1)[idx]
909
- else
910
- raise TypeError
911
- end
851
+ case args[0]
852
+ when Capybara::Node::Element
853
+ args[0]
854
+ when String, Hash
855
+ find(:frame, *args)
856
+ when Symbol
857
+ find(*args)
858
+ when Integer
859
+ idx = args[0]
860
+ all(:frame, minimum: idx + 1)[idx]
861
+ else
862
+ raise TypeError
912
863
  end
913
864
  end
914
865
 
915
- def _switch_to_window(window = nil, options= {})
916
- options, window = window, nil if window.is_a? Hash
917
-
866
+ def _switch_to_window(window = nil, **options)
918
867
  raise Capybara::ScopeError, "Window cannot be switched inside a `within_frame` block" if scopes.include?(:frame)
919
868
  raise Capybara::ScopeError, "Window cannot be switch inside a `within` block" unless scopes.last.nil?
920
869
 
@@ -928,9 +877,7 @@ module Capybara
928
877
  begin
929
878
  driver.window_handles.each do |handle|
930
879
  driver.switch_to_window handle
931
- if yield
932
- return Window.new(self, handle)
933
- end
880
+ return Window.new(self, handle) if yield
934
881
  end
935
882
  rescue => e
936
883
  driver.switch_to_window(original_window_handle)
@@ -942,6 +889,5 @@ module Capybara
942
889
  end
943
890
  end
944
891
  end
945
-
946
892
  end
947
893
  end