capybara 2.15.4 → 2.16.0

Sign up to get free protection for your applications and to get access to all the features.
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,