capybara 2.15.4 → 2.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: b58346a93d1b784ad960451eea0e16c2cdb5264a
4
- data.tar.gz: 6386e073e35c61290e7fdf77353d06f1b887af08
2
+ SHA256:
3
+ metadata.gz: ee26f641aae1c298d4f5c3d7e8a98376c133c1e2d6ac90b3a3a274ae0dddd13f
4
+ data.tar.gz: 12a92103cb8b03439ba345d2433385d5d8ec698713c1fda8d8d496c1d7a5db4a
5
5
  SHA512:
6
- metadata.gz: 5bf103e7ee612aa2ba02f530d77c7cd01430d43df2ac4221a40b8b415e1ccf7f41df84d3ea6795f6629f9d019521169a868dce350c26f4c65d764e92c743f13e
7
- data.tar.gz: e94720fb414e4715748628717a13270372e3e1f1238fb8d36e8811d0060782f86a1ff851fdb1b1e78e8624cd4e3d5c6cb72b6de7af5a5b9276bf4098e15e0717
6
+ metadata.gz: b63bffec8f36f684af468659992edc8368844838a7f92f0cacaca2cfb070c8d5d62baebc6308dc60bac7646a0e4a7221eca416014cbdc2a0f82ce189dedc1172
7
+ data.tar.gz: 26d2d8319225c85ef3ea5c1c546f321a92063adcf08a97ed1e530782e7af18aa9c7b2e8c59faaac2bf02c5a3d0d7d37d82a124dc1a0472605296745fe26f2ca8
data/History.md CHANGED
@@ -1,3 +1,19 @@
1
+ # Version 2.16.0
2
+ Release date: 2017-11-13
3
+
4
+ ### Added
5
+
6
+ * Attempt to move element into view when selenium doesn't correctly do it - See PR #1917 [Thomas Walpole]
7
+ * `current_path` matchers will now autodetect path vs url based on string to be matched. Deprecates
8
+ `:only_path` in favor of `:ignore_query` option [Thomas Walpole]
9
+ * Session#evaluate_async_script [Thomas Walpole]
10
+
11
+ ### Fixed
12
+
13
+ * Default prompt value when using headless Chrome works correctly [Thomas Walpole]
14
+ * Support new modal error returned by selenium-webdriver 3.7 for W3C drivers [Thomas Walpole]
15
+ * Calling `respond_to?` on the object passed to `Capybara.configure` block - Issue #1935
16
+
1
17
  # Version 2.15.4
2
18
  Release date: 2017-10-07
3
19
 
@@ -235,7 +251,7 @@ Release date: 2016-09-19
235
251
 
236
252
  Release date: 2016-08-25
237
253
 
238
- ###Fixed
254
+ ### Fixed
239
255
 
240
256
  * Fixed error message from have_text when text is not found but contains regex special characters [Ryunosuke Sato]
241
257
  * Warn when :exact option is passed that has no effect [Thomas Walpole]
data/README.md CHANGED
@@ -549,7 +549,7 @@ If you find yourself needing to use this a lot you may be better off adding a [c
549
549
  ```ruby
550
550
  find_field('First Name'){ |el| el['data-xyz'] == '123' }
551
551
  find("#img_loading"){ |img| img['complete'] == true }
552
- ````
552
+ ```
553
553
 
554
554
  **Note**: `find` will wait for an element to appear on the page, as explained in the
555
555
  Ajax section. If the element does not appear it will raise an error.
@@ -894,7 +894,7 @@ To permanently switch the current session to a different session
894
894
 
895
895
  ```ruby
896
896
  Capybara.session_name = "some other session"
897
- ````
897
+ ```
898
898
 
899
899
  ### <a name="using-sessions-manually"></a>Using sessions manually
900
900
 
@@ -1053,7 +1053,9 @@ In normal mode most of Capybara's configuration options are global settings whic
1053
1053
  if using multiple sessions and wanting to change a setting for only one of the sessions. To provide
1054
1054
  support for this type of usage Capybara now provides a "threadsafe" mode which can be enabled by setting
1055
1055
 
1056
- Capybara.threadsafe = true
1056
+ ```ruby
1057
+ Capybara.threadsafe = true
1058
+ ```
1057
1059
 
1058
1060
  This setting can only be changed before any sessions have been created. In "threadsafe" mode the following
1059
1061
  behaviors of Capybara change
@@ -1063,11 +1065,13 @@ behaviors of Capybara change
1063
1065
  `app`, `reuse_server`, `default_driver`, `javascript_driver`, and (obviously) `threadsafe`. Any drivers and servers
1064
1066
  registered through `register_driver` and `register_server` are also global.
1065
1067
 
1066
- my_session = Capybara::Session.new(:driver, some_app) do |config|
1067
- config.automatic_label_click = true # only set for my_session
1068
- end
1069
- my_session.config.default_max_wait_time = 10 # only set for my_session
1070
- Capybara.default_max_wait_time = 2 # will not change the default_max_wait in my_session
1068
+ ```ruby
1069
+ my_session = Capybara::Session.new(:driver, some_app) do |config|
1070
+ config.automatic_label_click = true # only set for my_session
1071
+ end
1072
+ my_session.config.default_max_wait_time = 10 # only set for my_session
1073
+ Capybara.default_max_wait_time = 2 # will not change the default_max_wait in my_session
1074
+ ```
1071
1075
 
1072
1076
  * `current_driver` and `session_name` are thread specific. This means that `using_session` and
1073
1077
  `using_driver` also only affect the current thread.
@@ -108,6 +108,7 @@ module Capybara
108
108
  end
109
109
  end
110
110
 
111
+ # @api private
111
112
  class ConfigureDeprecator
112
113
  def initialize(config)
113
114
  @config = config
@@ -125,7 +126,7 @@ module Capybara
125
126
  end
126
127
 
127
128
  def respond_to_missing?(m, include_private = false)
128
- @config.respond_to_missing?(m, include_private) || Capybara.respond_to_missing?(m, include_private)
129
+ @config.respond_to?(m) || Capybara.respond_to?(m) || super
129
130
  end
130
131
  end
131
132
  end
@@ -42,6 +42,10 @@ class Capybara::Driver::Base
42
42
  raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#evaluate_script'
43
43
  end
44
44
 
45
+ def evaluate_async_script(script, *args)
46
+ raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#evaluate_script_asnyc'
47
+ end
48
+
45
49
  def save_screenshot(path, options={})
46
50
  raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#save_screenshot'
47
51
  end
@@ -8,13 +8,13 @@ module Capybara
8
8
  infect_an_assertion "refute_#{assertion}", "wont_have_#{assertion}", :reverse
9
9
  end
10
10
 
11
- (%w(selector xpath css link button field select table checked_field unchecked_field).map do |assertion|
11
+ (%w(selector xpath css link button field select table checked_field unchecked_field).flat_map do |assertion|
12
12
  [["assert_#{assertion}", "must_have_#{assertion}"],
13
13
  ["refute_#{assertion}", "wont_have_#{assertion}"]]
14
- end.flatten(1) + %w(selector xpath css).map do |assertion|
14
+ end + %w(selector xpath css).flat_map do |assertion|
15
15
  [["assert_matches_#{assertion}", "must_match_#{assertion}"],
16
16
  ["refute_matches_#{assertion}", "wont_match_#{assertion}"]]
17
- end.flatten(1)).each do |(meth, new_name)|
17
+ end).each do |(meth, new_name)|
18
18
  self.class_eval <<-EOM, __FILE__, __LINE__ + 1
19
19
  def #{new_name} *args, &block
20
20
  ::Minitest::Expectation.new(self, ::Minitest::Spec.current).#{new_name}(*args, &block)
@@ -5,8 +5,8 @@ module Capybara
5
5
 
6
6
  ##
7
7
  #
8
- # Finds a button or link by id, text or value and clicks it. Also looks at image
9
- # alt text inside the link.
8
+ # Finds a button or link and clicks it. See {Capybara::Node::Actions#click_button} and
9
+ # {Capybara::Node::Actions#click_link} for what locator will match against for each type of element
10
10
  # @!macro waiting_behavior
11
11
  # If the driver is capable of executing JavaScript, +$0+ will wait for a set amount of time
12
12
  # and continuously retry finding the element until either the element is found or the time
@@ -16,7 +16,7 @@ module Capybara
16
16
  #
17
17
  # @overload click_link_or_button([locator], options)
18
18
  #
19
- # @param [String] locator Text, id or value of link or button
19
+ # @param [String] locator See {Capybara::Node::Actions#click_button} and {Capybara::Node::Actions#click_link}
20
20
  #
21
21
  # @return [Capybara::Node::Element] The element clicked
22
22
  #
@@ -80,7 +80,7 @@ module Capybara
80
80
  # @option options [String] :id Match fields that match the id attribute
81
81
  # @option options [String] :name Match fields that match the name attribute
82
82
  # @option options [String] :placeholder Match fields that match the placeholder attribute
83
- # @option options [String, Array<String>] :class Match links that match the class(es) provided
83
+ # @option options [String, Array<String>] :class Match fields that match the class(es) provided
84
84
  #
85
85
  # @return [Capybara::Node::Element] The element filled_in
86
86
  def fill_in(locator, options={})
@@ -108,7 +108,7 @@ module Capybara
108
108
  # @option options [String] :option Value of the radio_button to choose
109
109
  # @option options [String] :id Match fields that match the id attribute
110
110
  # @option options [String] :name Match fields that match the name attribute
111
- # @option options [String, Array<String>] :class Match links that match the class(es) provided
111
+ # @option options [String, Array<String>] :class Match fields that match the class(es) provided
112
112
  # @macro waiting_behavior
113
113
  # @macro label_click
114
114
  #
@@ -131,7 +131,7 @@ module Capybara
131
131
  # @option options [String] :option Value of the checkbox to select
132
132
  # @option options [String] id Match fields that match the id attribute
133
133
  # @option options [String] name Match fields that match the name attribute
134
- # @option options [String, Array<String>] :class Match links that match the class(es) provided
134
+ # @option options [String, Array<String>] :class Match fields that match the class(es) provided
135
135
  # @macro label_click
136
136
  # @macro waiting_behavior
137
137
  #
@@ -154,7 +154,7 @@ module Capybara
154
154
  # @option options [String] :option Value of the checkbox to deselect
155
155
  # @option options [String] id Match fields that match the id attribute
156
156
  # @option options [String] name Match fields that match the name attribute
157
- # @option options [String, Array<String>] :class Match links that match the class(es) provided
157
+ # @option options [String, Array<String>] :class Match fields that match the class(es) provided
158
158
  # @macro label_click
159
159
  # @macro waiting_behavior
160
160
  #
@@ -229,7 +229,7 @@ module Capybara
229
229
  # @option options [Boolean] multiple Match field which allows multiple file selection
230
230
  # @option options [String] id Match fields that match the id attribute
231
231
  # @option options [String] name Match fields that match the name attribute
232
- # @option options [String, Array<String>] :class Match links that match the class(es) provided
232
+ # @option options [String, Array<String>] :class Match fields that match the class(es) provided
233
233
  # @option options [true, Hash] make_visible A Hash of CSS styles to change before attempting to attach the file, if `true` { opacity: 1, display: 'block', visibility: 'visible' } is used (may not be supported by all drivers)
234
234
  #
235
235
  # @return [Capybara::Node::Element] The file field element
@@ -171,7 +171,7 @@ module Capybara
171
171
  # @option options [String] id Match buttons with the id provided
172
172
  # @option options [String] title Match buttons with the title provided
173
173
  # @option options [String] value Match buttons with the value provided
174
- # @option options [String, Array<String>] class Match links that match the class(es) provided
174
+ # @option options [String, Array<String>] class Match buttons that match the class(es) provided
175
175
  # @return [Capybara::Node::Element] The found element
176
176
  #
177
177
  def find_button(locator=nil, options={}, &optional_filter_block)
@@ -8,22 +8,25 @@ module Capybara
8
8
  def initialize(expected_path, options = {})
9
9
  super(options)
10
10
  @expected_path = expected_path
11
+ warn "DEPRECATED: The :only_path option is deprecated in favor of the :ignore_query option" if options.has_key?(:only_path)
12
+
11
13
  @options = {
12
- url: false,
13
- only_path: false }.merge(options)
14
+ url: !@expected_path.is_a?(Regexp) && !::Addressable::URI.parse(@expected_path || "").hostname.nil?,
15
+ only_path: false,
16
+ ignore_query: false }.merge(options)
14
17
  assert_valid_keys
15
18
  end
16
19
 
17
20
  def resolves_for?(session)
21
+ uri = ::Addressable::URI.parse(session.current_url)
22
+ uri.query = nil if uri && options[:ignore_query]
18
23
  @actual_path = if options[:url]
19
- session.current_url
24
+ uri.to_s
20
25
  else
21
- uri = ::Addressable::URI.parse(session.current_url)
22
-
23
26
  if options[:only_path]
24
- uri.path unless uri.nil? # Ensure the parsed url isn't nil.
27
+ uri && uri.path
25
28
  else
26
- uri.request_uri unless uri.nil? # Ensure the parsed url isn't nil.
29
+ uri && uri.request_uri
27
30
  end
28
31
  end
29
32
 
@@ -50,7 +53,7 @@ module Capybara
50
53
  end
51
54
 
52
55
  def valid_keys
53
- [:wait, :url, :only_path]
56
+ [:wait, :url, :only_path, :ignore_query]
54
57
  end
55
58
 
56
59
  def assert_valid_keys
@@ -17,7 +17,6 @@ module Capybara
17
17
  if args[0].is_a?(Symbol)
18
18
  @selector = Selector.all.fetch(args.shift) do |selector_type|
19
19
  raise ArgumentError, "Unknown selector type (:#{selector_type})"
20
- nil
21
20
  end
22
21
  @locator = args.shift
23
22
  else
@@ -26,7 +25,7 @@ module Capybara
26
25
  end
27
26
  @selector ||= Selector.all[session_options.default_selector]
28
27
 
29
- warn "Unused parameters passed to #{self.class.name} : #{args.to_s}" unless args.empty?
28
+ warn "Unused parameters passed to #{self.class.name} : #{args}" unless args.empty?
30
29
 
31
30
  # for compatibility with Capybara 2.0
32
31
  if session_options.exact_options and @selector == Selector.all[:option]
@@ -235,7 +234,7 @@ module Capybara
235
234
 
236
235
  def warn_exact_usage
237
236
  if options.has_key?(:exact) && !supports_exact?
238
- warn "The :exact option only has an effect on queries using the XPath#is method. Using it with the query \"#{expression.to_s}\" has no effect."
237
+ warn "The :exact option only has an effect on queries using the XPath#is method. Using it with the query \"#{expression}\" has no effect."
239
238
  end
240
239
  end
241
240
 
@@ -16,7 +16,7 @@ module Capybara
16
16
  @expected_text = Capybara::Helpers.normalize_whitespace(@expected_text)
17
17
  end
18
18
  @search_regexp = Capybara::Helpers.to_regexp(@expected_text, nil, exact?)
19
- warn "Unused parameters passed to #{self.class.name} : #{args.to_s}" unless args.empty?
19
+ warn "Unused parameters passed to #{self.class.name} : #{args}" unless args.empty?
20
20
  assert_valid_keys
21
21
  end
22
22
 
@@ -24,7 +24,6 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
24
24
 
25
25
  @w3c = ((defined?(Selenium::WebDriver::Remote::W3CCapabilities) && @browser.capabilities.is_a?(Selenium::WebDriver::Remote::W3CCapabilities)) ||
26
26
  (defined?(Selenium::WebDriver::Remote::W3C::Capabilities) && @browser.capabilities.is_a?(Selenium::WebDriver::Remote::W3C::Capabilities)))
27
-
28
27
  main = Process.pid
29
28
  at_exit do
30
29
  # Store the exit status of the test run since it goes away after calling the at_exit proc...
@@ -114,6 +113,12 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
114
113
  unwrap_script_result(result)
115
114
  end
116
115
 
116
+ def evaluate_async_script(script, *args)
117
+ browser.manage.timeouts.script_timeout = Capybara.default_max_wait_time
118
+ result = browser.execute_async_script(script, *args.map { |arg| arg.is_a?(Capybara::Selenium::Node) ? arg.native : arg} )
119
+ unwrap_script_result(result)
120
+ end
121
+
117
122
  def save_screenshot(path, _options={})
118
123
  browser.save_screenshot(path)
119
124
  end
@@ -164,7 +169,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
164
169
  begin
165
170
  @browser.switch_to.alert.accept
166
171
  sleep 0.25 # allow time for the modal to be handled
167
- rescue Selenium::WebDriver::Error::NoAlertPresentError
172
+ rescue modal_error
168
173
  # The alert is now gone - nothing to do
169
174
  end
170
175
  # try cleaning up the browser again
@@ -246,13 +251,15 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
246
251
  def accept_modal(_type, options={})
247
252
  if headless_chrome?
248
253
  raise ArgumentError, "Block that triggers the system modal is missing" unless block_given?
249
- insert_modal_handlers(true, options[:with], options[:text])
254
+ insert_modal_handlers(true, options[:with])
250
255
  yield
251
256
  find_headless_modal(options)
252
257
  else
253
258
  yield if block_given?
254
259
  modal = find_modal(options)
260
+
255
261
  modal.send_keys options[:with] if options[:with]
262
+
256
263
  message = modal.text
257
264
  modal.accept
258
265
  message
@@ -262,7 +269,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
262
269
  def dismiss_modal(_type, options={})
263
270
  if headless_chrome?
264
271
  raise ArgumentError, "Block that triggers the system modal is missing" unless block_given?
265
- insert_modal_handlers(false, options[:with], options[:text])
272
+ insert_modal_handlers(false, options[:with])
266
273
  yield
267
274
  find_headless_modal(options)
268
275
  else
@@ -342,6 +349,14 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
342
349
  options[:browser].to_s
343
350
  end
344
351
 
352
+ def modal_error
353
+ if defined?(Selenium::WebDriver::Error::NoSuchAlertError)
354
+ Selenium::WebDriver::Error::NoSuchAlertError
355
+ else
356
+ Selenium::WebDriver::Error::NoAlertPresentError
357
+ end
358
+ end
359
+
345
360
  def find_window(locator)
346
361
  handles = browser.window_handles
347
362
  return locator if handles.include? locator
@@ -359,7 +374,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
359
374
  raise Capybara::ElementNotFound, "Could not find a window identified by #{locator}"
360
375
  end
361
376
 
362
- def insert_modal_handlers(accept, response_text, expected_text=nil)
377
+ def insert_modal_handlers(accept, response_text)
363
378
  script = <<-JS
364
379
  if (typeof window.capybara === 'undefined') {
365
380
  window.capybara = {
@@ -391,13 +406,13 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
391
406
  }
392
407
  window.capybara.add_handler(modal_handler);
393
408
 
394
- window.alert = window.confirm = function(str) {
395
- window.capybara.handler_called(modal_handler, str);
409
+ window.alert = window.confirm = function(str = "") {
410
+ window.capybara.handler_called(modal_handler, str.toString());
396
411
  return #{accept ? 'true' : 'false'};
397
- };
398
- window.prompt = function(str) {
399
- window.capybara.handler_called(modal_handler, str);
400
- return #{accept ? "'#{response_text}'" : 'null'};
412
+ }
413
+ window.prompt = function(str = "", default_text = "") {
414
+ window.capybara.handler_called(modal_handler, str.toString());
415
+ return #{accept ? (response_text.nil? ? "default_text" : "'#{response_text}'") : 'null'};
401
416
  }
402
417
  JS
403
418
  execute_script script
@@ -420,7 +435,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
420
435
  # Actual wait time may be longer than specified
421
436
  wait = Selenium::WebDriver::Wait.new(
422
437
  timeout: options.fetch(:wait, session_options.default_max_wait_time) || 0 ,
423
- ignore: Selenium::WebDriver::Error::NoAlertPresentError)
438
+ ignore: modal_error)
424
439
  begin
425
440
  wait.until do
426
441
  alert = @browser.switch_to.alert
@@ -437,7 +452,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
437
452
  # Actual wait time may be longer than specified
438
453
  wait = Selenium::WebDriver::Wait.new(
439
454
  timeout: options.fetch(:wait, session_options.default_max_wait_time) || 0 ,
440
- ignore: Selenium::WebDriver::Error::NoAlertPresentError)
455
+ ignore: modal_error)
441
456
  begin
442
457
  wait.until do
443
458
  called, alert_text = evaluate_script('window.capybara && window.capybara.current_modal_status()')
@@ -100,6 +100,15 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
100
100
 
101
101
  def click
102
102
  native.click
103
+ rescue => e
104
+ if e.is_a?(::Selenium::WebDriver::Error::ElementClickInterceptedError) ||
105
+ e.message =~ /Other element would receive the click/
106
+ begin
107
+ driver.execute_script("arguments[0].scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'})", self)
108
+ rescue
109
+ end
110
+ end
111
+ raise e
103
112
  end
104
113
 
105
114
  def right_click
@@ -642,6 +642,24 @@ module Capybara
642
642
  element_script_result(result)
643
643
  end
644
644
 
645
+ ##
646
+ #
647
+ # Evaluate the given JavaScript and obtain the result from a callback function which will be passed as the last argument to the script.
648
+ #
649
+ # @param [String] script A string of JavaScript to evaluate
650
+ # @return [Object] The result of the evaluated JavaScript (may be driver specific)
651
+ #
652
+ def evaluate_async_script(script, *args)
653
+ @touched = true
654
+ result = if args.empty?
655
+ driver.evaluate_async_script(script)
656
+ else
657
+ raise Capybara::NotSupportedByDriverError, "The current driver does not support evaluate_async_script arguments" if driver.method(:evaluate_async_script).arity == 1
658
+ driver.evaluate_async_script(script, *args.map { |arg| arg.is_a?(Capybara::Node::Element) ? arg.base : arg} )
659
+ end
660
+ element_script_result(result)
661
+ end
662
+
645
663
  ##
646
664
  #
647
665
  # Execute the block, accepting a alert.
@@ -3,16 +3,18 @@ module Capybara
3
3
  module SessionMatchers
4
4
  ##
5
5
  # Asserts that the page has the given path.
6
- # By default this will compare against the path+query portion of the full url
6
+ # By default, if passed a full url this will compare against the full url,
7
+ # if passed a path only the path+query portion will be compared, if passed a regexp
8
+ # the comparison will depend on the :url option
7
9
  #
8
10
  # @!macro current_path_query_params
9
11
  # @overload $0(string, options = {})
10
12
  # @param string [String] The string that the current 'path' should equal
11
13
  # @overload $0(regexp, options = {})
12
14
  # @param regexp [Regexp] The regexp that the current 'path' should match to
13
- # @option options [Numeric] :wait (Capybara.default_max_wait_time) Maximum time that Capybara will wait for the current path to eq/match given string/regexp argument
14
- # @option options [Boolean] :url (false) Whether the compare should be done against the full url
15
- # @option options [Boolean] :only_path (false) Whether the compare should be done against just the path protion of the url
15
+ # @option options [Boolean] :url (true if `string` ia a full url, otherwise false) Whether the compare should be done against the full current url or just the path
16
+ # @option options [Boolean] :ignore_query (false) Whether the query portion of the current url/path should be ignored
17
+ # @option options [Numeric] :wait (Capybara.default_max_wait_time) Maximum time that Capybara will wait for the current url/path to eq/match given string/regexp argument
16
18
  # @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
17
19
  # @return [true]
18
20
  #
@@ -22,7 +24,9 @@ module Capybara
22
24
 
23
25
  ##
24
26
  # Asserts that the page doesn't have the given path.
25
- # By default this will compare against the path+query portion of the full url
27
+ # By default, if passed a full url this will compare against the full url,
28
+ # if passed a path only the path+query portion will be compared, if passed a regexp
29
+ # the comparison will depend on the :url option
26
30
  #
27
31
  # @macro current_path_query_params
28
32
  # @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
@@ -34,7 +38,9 @@ module Capybara
34
38
 
35
39
  ##
36
40
  # Checks if the page has the given path.
37
- # By default this will compare against the path+query portion of the full url
41
+ # By default, if passed a full url this will compare against the full url,
42
+ # if passed a path only the path+query portion will be compared, if passed a regexp
43
+ # the comparison will depend on the :url option
38
44
  #
39
45
  # @macro current_path_query_params
40
46
  # @return [Boolean]
@@ -47,7 +53,9 @@ module Capybara
47
53
 
48
54
  ##
49
55
  # Checks if the page doesn't have the given path.
50
- # By default this will compare against the path+query portion of the full url
56
+ # By default, if passed a full url this will compare against the full url,
57
+ # if passed a path only the path+query portion will be compared, if passed a regexp
58
+ # the comparison will depend on the :url option
51
59
  #
52
60
  # @macro current_path_query_params
53
61
  # @return [Boolean]
@@ -109,6 +109,14 @@ $(function() {
109
109
  $(this).attr('response', response);
110
110
  }
111
111
  });
112
+ $('#open-prompt-with-default').click(function() {
113
+ var response = prompt('Prompt opened', 'Default value!');
114
+ if(response === null) {
115
+ $(this).attr('response', 'dismissed');
116
+ } else {
117
+ $(this).attr('response', response);
118
+ }
119
+ });
112
120
  $('#open-twice').click(function() {
113
121
  if (confirm('Are you sure?')) {
114
122
  if (!confirm('Are you really sure?')) {
@@ -11,6 +11,13 @@ Capybara::SpecHelper.spec '#accept_prompt', requires: [:modals] do
11
11
  expect(@session).to have_xpath("//a[@id='open-prompt' and @response='']")
12
12
  end
13
13
 
14
+ it "should accept the prompt with no message when there is a default" do
15
+ @session.accept_prompt do
16
+ @session.click_link('Open defaulted prompt')
17
+ end
18
+ expect(@session).to have_xpath("//a[@id='open-prompt-with-default' and @response='Default value!']")
19
+ end
20
+
14
21
  it "should return the message presented" do
15
22
  message = @session.accept_prompt do
16
23
  @session.click_link('Open prompt')
@@ -25,6 +32,21 @@ Capybara::SpecHelper.spec '#accept_prompt', requires: [:modals] do
25
32
  expect(@session).to have_xpath("//a[@id='open-prompt' and @response='the response']")
26
33
  end
27
34
 
35
+ it "should accept the prompt with a response when there is a default" do
36
+ @session.accept_prompt with: 'the response' do
37
+ @session.click_link('Open defaulted prompt')
38
+ end
39
+ expect(@session).to have_xpath("//a[@id='open-prompt-with-default' and @response='the response']")
40
+ end
41
+
42
+ it "should accept the prompt with a blank response when there is a default" do
43
+ pending "Geckodriver doesn't set a blank response currently" if @session.respond_to?(:mode) && @session.mode.to_s == "selenium_marionette"
44
+ @session.accept_prompt with: '' do
45
+ @session.click_link('Open defaulted prompt')
46
+ end
47
+ expect(@session).to have_xpath("//a[@id='open-prompt-with-default' and @response='']")
48
+ end
49
+
28
50
  it "should accept the prompt if the message matches" do
29
51
  @session.accept_prompt 'Prompt opened', with: 'matched' do
30
52
  @session.click_link('Open prompt')
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ Capybara::SpecHelper.spec "#evaluate_async_script", requires: [:js] do
3
+ it "should evaluate the given script and return whatever it produces" do
4
+ @session.visit('/with_js')
5
+ expect(@session.evaluate_async_script("arguments[0](4)")).to eq(4)
6
+ end
7
+
8
+ it "should support passing elements as arguments to the script", requires: [:js, :es_args] do
9
+ @session.visit('/with_js')
10
+ el = @session.find(:css, '#drag p')
11
+ result = @session.evaluate_async_script("arguments[2]([arguments[0].innerText, arguments[1]])", el, "Doodle Funk")
12
+ expect(result).to eq ["This is a draggable element.", "Doodle Funk"]
13
+ end
14
+
15
+ it "should support returning elements after asynchronous operation", requires: [:js, :es_args] do
16
+ @session.visit('/with_js')
17
+ @session.find(:css, '#change') # ensure page has loaded and element is available
18
+ el = @session.evaluate_async_script("var cb = arguments[0]; setTimeout(function(){ cb(document.getElementById('change')) }, 100)")
19
+ expect(el).to be_instance_of(Capybara::Node::Element)
20
+ expect(el).to eq(@session.find(:css, '#change'))
21
+ end
22
+ end
@@ -20,7 +20,7 @@ Capybara::SpecHelper.spec "#evaluate_script", requires: [:js] do
20
20
 
21
21
  it "should support returning elements", requires: [:js, :es_args] do
22
22
  @session.visit('/with_js')
23
- el = @session.find(:css, '#change')
23
+ @session.find(:css, '#change') # ensure page has loaded and element is available
24
24
  el = @session.evaluate_script("document.getElementById('change')")
25
25
  expect(el).to be_instance_of(Capybara::Node::Element)
26
26
  expect(el).to eq(@session.find(:css, '#change'))
@@ -37,20 +37,47 @@ Capybara::SpecHelper.spec '#has_current_path?' do
37
37
  expect(@session).to have_current_path('/with_js?test=test')
38
38
  end
39
39
 
40
- it "should compare the full url" do
40
+ it "should compare the full url if url: true is used" do
41
41
  expect(@session).to have_current_path(%r{\Ahttp://[^/]*/with_js\Z}, url: true)
42
+ domain_port = if @session.respond_to?(:server) && @session.server
43
+ "#{@session.server.host}:#{@session.server.port}"
44
+ else
45
+ "www.example.com"
46
+ end
47
+ expect(@session).to have_current_path("http://#{domain_port}/with_js", url: true)
48
+ end
49
+
50
+ it "should not compare the full url if url: true is not passed" do
51
+ expect(@session).to have_current_path(%r{^/with_js\Z})
52
+ expect(@session).to have_current_path('/with_js')
53
+ end
54
+
55
+ it "should not compare the full url if url: false is passed" do
56
+ expect(@session).to have_current_path(%r{^/with_js\Z}, url: false)
57
+ expect(@session).to have_current_path('/with_js', url: false)
58
+ end
59
+
60
+ it "should default to full url if value is a url" do
61
+ url = @session.current_url
62
+ expect(url).to match /with_js$/
63
+ expect(@session).to have_current_path(url)
64
+ expect(@session).not_to have_current_path("http://www.not_example.com/with_js")
42
65
  end
43
66
 
44
67
  it "should ignore the query" do
45
68
  @session.visit('/with_js?test=test')
46
69
  expect(@session).to have_current_path('/with_js?test=test')
47
70
  expect(@session).to have_current_path('/with_js', only_path: true)
71
+ expect(@session).to have_current_path('/with_js', ignore_query: true)
72
+ uri = ::Addressable::URI.parse(@session.current_url)
73
+ uri.query = nil
74
+ expect(@session).to have_current_path(uri.to_s, ignore_query: true)
48
75
  end
49
76
 
50
77
  it "should not allow url and only_path at the same time" do
51
78
  expect {
52
79
  expect(@session).to have_current_path('/with_js', url: true, only_path: true)
53
- }. to raise_error ArgumentError
80
+ }.to raise_error ArgumentError
54
81
  end
55
82
 
56
83
  it "should not raise an exception if the current_url is nil" do
@@ -59,12 +86,17 @@ Capybara::SpecHelper.spec '#has_current_path?' do
59
86
  # Without only_path option
60
87
  expect {
61
88
  expect(@session).to have_current_path(nil)
62
- }. not_to raise_exception
89
+ }.not_to raise_exception
63
90
 
64
91
  # With only_path option
65
92
  expect {
66
93
  expect(@session).to have_current_path(nil, only_path: true)
67
- }. not_to raise_exception
94
+ }.not_to raise_exception
95
+
96
+ # With ignore_query option
97
+ expect {
98
+ expect(@session).to have_current_path(nil, ignore_query: true)
99
+ }.not_to raise_exception
68
100
  end
69
101
  end
70
102
 
@@ -105,5 +137,10 @@ Capybara::SpecHelper.spec '#has_no_current_path?' do
105
137
  expect {
106
138
  expect(@session).not_to have_current_path('/with_js', only_path: true)
107
139
  }. not_to raise_exception
140
+
141
+ # With ignore_query option
142
+ expect {
143
+ expect(@session).not_to have_current_path('/with_js', ignore_query: true)
144
+ }. not_to raise_exception
108
145
  end
109
146
  end
@@ -0,0 +1,17 @@
1
+ <html>
2
+ <head>
3
+ <style>
4
+ header { height: 45px; position: fixed; top: 0; background-color: red; width: 100%;}
5
+ footer { height: 45px; position: fixed; bottom: 0; background-color: red; width: 100%;}
6
+ #main { margin: 45px;}
7
+ #tall { display: block; height: 2000px;}
8
+ </style>
9
+ </head>
10
+ <body>
11
+ <header>My headers</header>
12
+ <div id="main">
13
+ <div id="tall">A tall block</div>
14
+ <a href="/">Go to root</a>
15
+ </div>
16
+ <footer>My footer</footer>
17
+ </body>
@@ -96,6 +96,10 @@
96
96
  <a href="#" id="open-prompt">Open prompt</a>
97
97
  </p>
98
98
 
99
+ <p>
100
+ <a href="#" id="open-prompt-with-default">Open defaulted prompt</a>
101
+ </p>
102
+
99
103
  <p>
100
104
  <input id="disable-on-click"/>
101
105
  </p>
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Capybara
3
- VERSION = '2.15.4'
3
+ VERSION = '2.16.0'
4
4
  end
@@ -14,7 +14,7 @@ Capybara.register_driver :selenium_marionette do |app|
14
14
  Capybara::Selenium::Driver.new(
15
15
  app,
16
16
  browser: :firefox,
17
- desired_capabilities: {:marionette => true},
17
+ desired_capabilities: {marionette: true, 'moz:webdriverClick': true},
18
18
  options: browser_options
19
19
  # Get a trace level log from geckodriver
20
20
  # :driver_opts => { args: ['-vv'] }
@@ -55,7 +55,7 @@ end
55
55
 
56
56
  RSpec.describe Capybara::Selenium::Driver do
57
57
  before do
58
- @driver = Capybara::Selenium::Driver.new(TestApp, browser: :firefox)
58
+ @driver = Capybara::Selenium::Driver.new(TestApp, browser: :firefox, options: browser_options)
59
59
  end
60
60
 
61
61
  describe '#quit' do
@@ -61,7 +61,7 @@ RSpec.shared_examples "Capybara::Session" do |session, mode|
61
61
  skip "Headless Chrome doesn't support blockless modal methods" if @session.driver.send(:headless_chrome?)
62
62
  @session.click_link('Open alert')
63
63
  @session.accept_alert
64
- expect{@session.driver.browser.switch_to.alert}.to raise_error(Selenium::WebDriver::Error::NoAlertPresentError)
64
+ expect{@session.driver.browser.switch_to.alert}.to raise_error(@session.driver.send(:modal_error))
65
65
  end
66
66
 
67
67
  it "raises if block is missing" do
@@ -180,6 +180,17 @@ RSpec.shared_examples "Capybara::Session" do |session, mode|
180
180
  }
181
181
  })
182
182
  end
183
+
184
+ describe "#evaluate_async_script" do
185
+ it "will timeout if the script takes too long" do
186
+ @session.visit('/with_js')
187
+ expect do
188
+ @session.using_wait_time(1) do
189
+ @session.evaluate_async_script("var cb = arguments[0]; setTimeout(function(){ cb(null) }, 3000)")
190
+ end
191
+ end.to raise_error Selenium::WebDriver::Error::ScriptTimeoutError
192
+ end
193
+ end
183
194
  end
184
195
 
185
196
  describe "Element#inspect" do
@@ -191,5 +202,17 @@ RSpec.shared_examples "Capybara::Session" do |session, mode|
191
202
  expect(el.inspect).to eq "Obsolete #<Capybara::Node::Element>"
192
203
  end
193
204
  end
205
+
206
+ describe "Element#click" do
207
+ it "should handle fixed headers/footers" do
208
+ if mode == :selenium_marionette && !(ENV['TRAVIS_ALLOW_FAILURE']=='true')
209
+ pending "geckodriver/firefox/marionette just fail to click without reporting an error - no way for us to detect/catch"
210
+ end
211
+ @session.visit('/with_fixed_header_footer')
212
+ # @session.click_link('Go to root')
213
+ @session.find(:link, 'Go to root').click
214
+ expect(@session).to have_current_path('/')
215
+ end
216
+ end
194
217
  end
195
218
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: capybara
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.15.4
4
+ version: 2.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas Walpole
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain:
12
12
  - gem-public_cert.pem
13
- date: 2017-10-07 00:00:00.000000000 Z
13
+ date: 2017-11-13 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: nokogiri
@@ -376,6 +376,7 @@ files:
376
376
  - lib/capybara/spec/session/element/match_css_spec.rb
377
377
  - lib/capybara/spec/session/element/match_xpath_spec.rb
378
378
  - lib/capybara/spec/session/element/matches_selector_spec.rb
379
+ - lib/capybara/spec/session/evaluate_async_script_spec.rb
379
380
  - lib/capybara/spec/session/evaluate_script_spec.rb
380
381
  - lib/capybara/spec/session/execute_script_spec.rb
381
382
  - lib/capybara/spec/session/fill_in_spec.rb
@@ -447,6 +448,7 @@ files:
447
448
  - lib/capybara/spec/views/tables.erb
448
449
  - lib/capybara/spec/views/with_base_tag.erb
449
450
  - lib/capybara/spec/views/with_count.erb
451
+ - lib/capybara/spec/views/with_fixed_header_footer.erb
450
452
  - lib/capybara/spec/views/with_hover.erb
451
453
  - lib/capybara/spec/views/with_html.erb
452
454
  - lib/capybara/spec/views/with_html_entities.erb
@@ -506,7 +508,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
506
508
  version: '0'
507
509
  requirements: []
508
510
  rubyforge_project:
509
- rubygems_version: 2.6.13
511
+ rubygems_version: 2.7.0
510
512
  signing_key:
511
513
  specification_version: 4
512
514
  summary: Capybara aims to simplify the process of integration testing Rack applications,