capybara 2.11.0 → 2.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +30 -4
  3. data/README.md +4 -0
  4. data/lib/capybara.rb +4 -2
  5. data/lib/capybara/driver/base.rb +2 -2
  6. data/lib/capybara/helpers.rb +8 -2
  7. data/lib/capybara/node/actions.rb +52 -1
  8. data/lib/capybara/node/document_matchers.rb +1 -0
  9. data/lib/capybara/node/finders.rb +2 -1
  10. data/lib/capybara/node/matchers.rb +54 -0
  11. data/lib/capybara/node/simple.rb +1 -1
  12. data/lib/capybara/queries/current_path_query.rb +4 -2
  13. data/lib/capybara/queries/selector_query.rb +23 -3
  14. data/lib/capybara/queries/text_query.rb +15 -7
  15. data/lib/capybara/queries/title_query.rb +2 -2
  16. data/lib/capybara/rack_test/form.rb +1 -1
  17. data/lib/capybara/rack_test/node.rb +4 -4
  18. data/lib/capybara/result.rb +2 -2
  19. data/lib/capybara/selector.rb +16 -4
  20. data/lib/capybara/selenium/driver.rb +27 -22
  21. data/lib/capybara/selenium/node.rb +10 -1
  22. data/lib/capybara/session.rb +91 -30
  23. data/lib/capybara/spec/session/accept_prompt_spec.rb +3 -0
  24. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +94 -0
  25. data/lib/capybara/spec/session/assert_current_path.rb +12 -0
  26. data/lib/capybara/spec/session/attach_file_spec.rb +30 -0
  27. data/lib/capybara/spec/session/click_link_spec.rb +12 -1
  28. data/lib/capybara/spec/session/current_url_spec.rb +8 -0
  29. data/lib/capybara/spec/session/evaluate_script_spec.rb +14 -0
  30. data/lib/capybara/spec/session/execute_script_spec.rb +13 -0
  31. data/lib/capybara/spec/session/fill_in_spec.rb +6 -0
  32. data/lib/capybara/spec/session/find_field_spec.rb +2 -0
  33. data/lib/capybara/spec/session/find_spec.rb +3 -3
  34. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +103 -0
  35. data/lib/capybara/spec/session/{within_frame_spec.rb → frame/within_frame_spec.rb} +12 -0
  36. data/lib/capybara/spec/session/has_current_path_spec.rb +28 -0
  37. data/lib/capybara/spec/session/has_selector_spec.rb +21 -0
  38. data/lib/capybara/spec/session/has_text_spec.rb +13 -1
  39. data/lib/capybara/spec/session/has_title_spec.rb +15 -0
  40. data/lib/capybara/spec/session/node_spec.rb +34 -1
  41. data/lib/capybara/spec/session/within_spec.rb +7 -0
  42. data/lib/capybara/spec/spec_helper.rb +4 -0
  43. data/lib/capybara/spec/views/form.erb +48 -0
  44. data/lib/capybara/spec/views/with_js.erb +5 -0
  45. data/lib/capybara/spec/views/within_frames.erb +1 -1
  46. data/lib/capybara/version.rb +1 -1
  47. data/lib/capybara/window.rb +1 -1
  48. data/spec/capybara_spec.rb +2 -2
  49. data/spec/rack_test_spec.rb +10 -0
  50. data/spec/result_spec.rb +3 -3
  51. data/spec/rspec/shared_spec_matchers.rb +1 -1
  52. data/spec/session_spec.rb +10 -0
  53. data/spec/shared_selenium_session.rb +2 -1
  54. data/spec/spec_helper.rb +2 -0
  55. metadata +7 -4
@@ -9,7 +9,7 @@ module Capybara
9
9
  unless @expected_title.is_a?(Regexp)
10
10
  @expected_title = Capybara::Helpers.normalize_whitespace(@expected_title)
11
11
  end
12
- @search_regexp = Capybara::Helpers.to_regexp(@expected_title)
12
+ @search_regexp = Capybara::Helpers.to_regexp(@expected_title, nil, options.fetch(:exact, false))
13
13
  assert_valid_keys
14
14
  end
15
15
 
@@ -34,7 +34,7 @@ module Capybara
34
34
  end
35
35
 
36
36
  def valid_keys
37
- [:wait]
37
+ [:wait, :exact]
38
38
  end
39
39
  end
40
40
  end
@@ -65,7 +65,7 @@ class Capybara::RackTest::Form < Capybara::RackTest::Node
65
65
  merge_param!(params, field['name'].to_s, (option['value'] || option.text).to_s) if option
66
66
  end
67
67
  when 'textarea'
68
- merge_param!(params, field['name'].to_s, field.text.to_s.gsub(/\n/, "\r\n"))
68
+ merge_param!(params, field['name'].to_s, field['_capybara_raw_value'].to_s.gsub(/\n/, "\r\n"))
69
69
  end
70
70
  end
71
71
  merge_param!(params, button[:name], button[:value] || "") if button[:name]
@@ -18,7 +18,7 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
18
18
 
19
19
  def set(value)
20
20
  if (Array === value) && !multiple?
21
- raise ArgumentError.new "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
21
+ raise TypeError.new "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
22
22
  end
23
23
 
24
24
  if radio?
@@ -31,7 +31,7 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
31
31
  if self[:readonly]
32
32
  warn "Attempt to set readonly element with value: #{value} \n * This will raise an exception in a future version of Capybara"
33
33
  else
34
- native.content = value.to_s
34
+ native['_capybara_raw_value'] = value.to_s
35
35
  end
36
36
  end
37
37
  end
@@ -91,9 +91,9 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
91
91
 
92
92
  def disabled?
93
93
  if %w(option optgroup).include? tag_name
94
- string_node.disabled? || find_xpath("parent::*")[0].disabled?
94
+ string_node.disabled? || find_xpath("parent::*[self::optgroup or self::select]")[0].disabled?
95
95
  else
96
- string_node.disabled?
96
+ string_node.disabled? || !find_xpath("parent::fieldset[@disabled] | ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]").empty?
97
97
  end
98
98
  end
99
99
 
@@ -28,11 +28,11 @@ module Capybara
28
28
  @result_cache = []
29
29
  @results_enum = lazy_select_elements { |node| query.matches_filters?(node) }
30
30
  @query = query
31
- # JRuby has an issue with eagerly finding next in lazy enumerators which
31
+ # JRuby < 9.1.6.0 has an issue with eagerly finding next in lazy enumerators which
32
32
  # causes a concurrency issue with network requests here
33
33
  # https://github.com/jruby/jruby/issues/4212
34
34
  # Just force all the results to be evaluated
35
- full_results if RUBY_PLATFORM == 'java'
35
+ full_results if RUBY_PLATFORM == 'java' && (Gem::Version.new(JRUBY_VERSION) < Gem::Version.new('9.1.6.0'))
36
36
  end
37
37
 
38
38
  def_delegators :full_results, :size, :length, :last, :values_at, :inspect, :sample
@@ -123,11 +123,16 @@ end
123
123
  # @filter [String] :title Matches the title attribute
124
124
  # @filter [String] :alt Matches the alt attribute of a contained img element
125
125
  # @filter [String] :class Matches the class(es) provided
126
- # @filter [String, Regexp] :href Matches the normalized href of the link
126
+ # @filter [String, Regexp,nil] :href Matches the normalized href of the link, if nil will find <a> elements with no href attribute
127
127
  #
128
128
  Capybara.add_selector(:link) do
129
129
  xpath(:title, :alt) do |locator, options={}|
130
- xpath = XPath.descendant(:a)[XPath.attr(:href)]
130
+ xpath = XPath.descendant(:a)
131
+ xpath = if options.fetch(:href, true).nil?
132
+ xpath[~XPath.attr(:href)]
133
+ else
134
+ xpath[XPath.attr(:href)]
135
+ end
131
136
  unless locator.nil?
132
137
  locator = locator.to_s
133
138
  matchers = XPath.attr(:id).equals(locator) |
@@ -143,14 +148,21 @@ Capybara.add_selector(:link) do
143
148
  end
144
149
 
145
150
  filter(:href) do |node, href|
146
- if href.is_a? Regexp
151
+ case href
152
+ when nil
153
+ true
154
+ when Regexp
147
155
  node[:href].match href
148
156
  else
149
157
  node.first(:xpath, XPath.axis(:self)[XPath.attr(:href).equals(href.to_s)], minimum: 0)
150
158
  end
151
159
  end
152
160
 
153
- describe { |options| " with href #{options[:href].inspect}" if options[:href] }
161
+ describe do |options|
162
+ desc = String.new()
163
+ desc << " with href #{options[:href].inspect}" if options[:href]
164
+ desc << " with no href attribute" if options.fetch(:href, true).nil?
165
+ end
154
166
  end
155
167
 
156
168
  ##
@@ -86,12 +86,12 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
86
86
  def wait?; true; end
87
87
  def needs_server?; true; end
88
88
 
89
- def execute_script(script)
90
- browser.execute_script script
89
+ def execute_script(script, *args)
90
+ browser.execute_script(script, *args.map { |arg| arg.is_a?(Capybara::Selenium::Node) ? arg.native : arg} )
91
91
  end
92
92
 
93
- def evaluate_script(script)
94
- browser.execute_script "return #{script}"
93
+ def evaluate_script(script, *args)
94
+ browser.execute_script("return #{script}", *args.map { |arg| arg.is_a?(Capybara::Selenium::Node) ? arg.native : arg} )
95
95
  end
96
96
 
97
97
  def save_screenshot(path, _options={})
@@ -213,24 +213,6 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
213
213
  browser.switch_to.window handle
214
214
  end
215
215
 
216
- # @api private
217
- def find_window(locator)
218
- handles = browser.window_handles
219
- return locator if handles.include? locator
220
-
221
- original_handle = browser.window_handle
222
- handles.each do |handle|
223
- switch_to_window(handle)
224
- if (locator == browser.execute_script("return window.name") ||
225
- browser.title.include?(locator) ||
226
- browser.current_url.include?(locator))
227
- switch_to_window(original_handle)
228
- return handle
229
- end
230
- end
231
- raise Capybara::ElementNotFound, "Could not find a window identified by #{locator}"
232
- end
233
-
234
216
  def within_window(locator)
235
217
  handle = find_window(locator)
236
218
  browser.switch_to.window(handle) { yield }
@@ -277,6 +259,29 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
277
259
  Selenium::WebDriver::Error::NoSuchWindowError
278
260
  end
279
261
 
262
+ # @api private
263
+ def find_window(locator)
264
+ handles = browser.window_handles
265
+ return locator if handles.include? locator
266
+
267
+ original_handle = browser.window_handle
268
+ handles.each do |handle|
269
+ switch_to_window(handle)
270
+ if (locator == browser.execute_script("return window.name") ||
271
+ browser.title.include?(locator) ||
272
+ browser.current_url.include?(locator))
273
+ switch_to_window(original_handle)
274
+ return handle
275
+ end
276
+ end
277
+ raise Capybara::ElementNotFound, "Could not find a window identified by #{locator}"
278
+ end
279
+
280
+ #@api private
281
+ def marionette?
282
+ (options[:browser].to_s == "firefox") && browser.capabilities.is_a?(Selenium::WebDriver::Remote::W3CCapabilities)
283
+ end
284
+
280
285
  # @deprecated This method is being removed
281
286
  def browser_initialized?
282
287
  super && !@browser.nil?
@@ -138,7 +138,16 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
138
138
  alias :checked? :selected?
139
139
 
140
140
  def disabled?
141
- !native.enabled?
141
+ # workaround for selenium-webdriver/geckodriver reporting elements as enabled when they are nested in disabling elements
142
+ if driver.marionette?
143
+ if %w(option optgroup).include? tag_name
144
+ !native.enabled? || find_xpath("parent::*[self::optgroup or self::select]")[0].disabled?
145
+ else
146
+ !native.enabled? || !find_xpath("parent::fieldset[@disabled] | ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]").empty?
147
+ end
148
+ else
149
+ !native.enabled?
150
+ end
142
151
  end
143
152
 
144
153
  def readonly?
@@ -41,6 +41,7 @@ module Capybara
41
41
  :has_no_table?, :has_table?, :unselect, :has_select?, :has_no_select?,
42
42
  :has_selector?, :has_no_selector?, :click_on, :has_no_checked_field?,
43
43
  :has_no_unchecked_field?, :query, :assert_selector, :assert_no_selector,
44
+ :assert_all_of_selectors, :assert_none_of_selectors,
44
45
  :refute_selector, :assert_text, :assert_no_text
45
46
  ]
46
47
  # @api private
@@ -50,8 +51,8 @@ module Capybara
50
51
  SESSION_METHODS = [
51
52
  :body, :html, :source, :current_url, :current_host, :current_path,
52
53
  :execute_script, :evaluate_script, :visit, :go_back, :go_forward,
53
- :within, :within_fieldset, :within_table, :within_frame, :current_window,
54
- :windows, :open_new_window, :switch_to_window, :within_window, :window_opened_by,
54
+ :within, :within_element, :within_fieldset, :within_table, :within_frame, :switch_to_frame,
55
+ :current_window, :windows, :open_new_window, :switch_to_window, :within_window, :window_opened_by,
55
56
  :save_page, :save_and_open_page, :save_screenshot,
56
57
  :save_and_open_screenshot, :reset_session!, :response_headers,
57
58
  :status_code, :current_scope,
@@ -67,6 +68,7 @@ module Capybara
67
68
  attr_accessor :synchronized
68
69
 
69
70
  def initialize(mode, app=nil)
71
+ raise TypeError, "The second parameter to Session::new should be a rack app if passed." if app && !app.respond_to?(:call)
70
72
  @mode = mode
71
73
  @app = app
72
74
  if Capybara.run_server and @app and driver.needs_server?
@@ -171,6 +173,10 @@ module Capybara
171
173
  def current_path
172
174
  # Addressable parsing is more lenient than URI
173
175
  uri = Addressable::URI.parse(current_url)
176
+
177
+ # If current_url ends up being nil, won't be able to call .path on a NilClass.
178
+ return nil if uri.nil?
179
+
174
180
  # Addressable doesn't support opaque URIs - we want nil here
175
181
  return nil if uri.scheme == "about"
176
182
  path = uri.path
@@ -306,6 +312,7 @@ module Capybara
306
312
  scopes.pop
307
313
  end
308
314
  end
315
+ alias_method :within_element, :within
309
316
 
310
317
  ##
311
318
  #
@@ -331,6 +338,42 @@ module Capybara
331
338
  end
332
339
  end
333
340
 
341
+ ##
342
+ #
343
+ # Switch to the given frame
344
+ #
345
+ # If you use this method you are responsible for making sure you switch back to the parent frame when done in the frame changed to.
346
+ # Capybara::Session#within_frame is preferred over this method and should be used when possible.
347
+ # May not be supported by all drivers.
348
+ #
349
+ # @overload switch_to_frame(element)
350
+ # @param [Capybara::Node::Element] iframe/frame element to switch to
351
+ # @overload switch_to_frame(:parent)
352
+ # Switch to the parent element
353
+ # @overload switch_to_frame(:top)
354
+ # Switch to the top level document
355
+ #
356
+ def switch_to_frame(frame)
357
+ case frame
358
+ when Capybara::Node::Element
359
+ driver.switch_to_frame(frame)
360
+ scopes.push(:frame)
361
+ when :parent
362
+ raise Capybara::ScopeError, "`switch_to_frame(:parent)` cannot be called from inside a descendant frame's "\
363
+ "`within` block." if scopes.last() != :frame
364
+ scopes.pop
365
+ driver.switch_to_frame(:parent)
366
+ when :top
367
+ idx = scopes.index(:frame)
368
+ if idx
369
+ raise Capybara::ScopeError, "`switch_to_frame(:top)` cannot be called from inside a descendant frame's "\
370
+ "`within` block." if scopes.slice(idx..-1).any? {|scope| ![:frame, nil].include?(scope)}
371
+ scopes.slice!(idx..-1)
372
+ driver.switch_to_frame(:top)
373
+ end
374
+ end
375
+ end
376
+
334
377
  ##
335
378
  #
336
379
  # Execute the given block within the given iframe using given frame, frame name/id or index.
@@ -338,45 +381,50 @@ module Capybara
338
381
  #
339
382
  # @overload within_frame(element)
340
383
  # @param [Capybara::Node::Element] frame element
341
- # @overload within_frame(name)
342
- # @param [String] name name/id of a frame
384
+ # @overload within_frame([kind = :frame], locator, options = {})
385
+ # @param [Symobl] kind Optional selector type (:css, :xpath, :field, etc.) - Defaults to :frame
386
+ # @param [String] locator The locator for the given selector kind. For :frame this is the name/id of a frame/iframe element
343
387
  # @overload within_frame(index)
344
- # @param [Integer] index index of a frame
345
- #
346
- def within_frame(locator)
347
- scopes.push(nil)
348
-
349
- #support older driver frame api for now
350
- frame = case locator
351
- when Capybara::Node::Element
352
- locator
353
- when String
354
- find(:frame, locator)
355
- when Integer
356
- all(:frame, minimum: locator+1)[locator]
357
- else
358
- raise ArgumentError
388
+ # @param [Integer] index index of a frame (0 based)
389
+ def within_frame(*args)
390
+ frame = within(document) do # Previous 2.x versions ignored current scope when finding frames - consider changing in 3.0
391
+ case args[0]
392
+ when Capybara::Node::Element
393
+ args[0]
394
+ when String, Hash
395
+ find(:frame, *args)
396
+ when Symbol
397
+ find(*args)
398
+ when Integer
399
+ idx = args[0]
400
+ all(:frame, minimum: idx+1)[idx]
401
+ else
402
+ raise TypeError
403
+ end
359
404
  end
360
405
 
361
406
  begin
362
- driver.switch_to_frame(frame)
407
+ switch_to_frame(frame)
363
408
  begin
364
409
  yield
365
410
  ensure
366
- driver.switch_to_frame(:parent)
411
+ switch_to_frame(:parent)
367
412
  end
368
413
  rescue Capybara::NotSupportedByDriverError
369
414
  # Support older driver frame API for now
370
415
  if driver.respond_to?(:within_frame)
371
- driver.within_frame(frame) do
372
- yield
416
+ begin
417
+ scopes.push(:frame)
418
+ driver.within_frame(frame) do
419
+ yield
420
+ end
421
+ ensure
422
+ scopes.pop
373
423
  end
374
424
  else
375
425
  raise
376
426
  end
377
427
  end
378
- ensure
379
- scopes.pop
380
428
  end
381
429
 
382
430
  ##
@@ -560,10 +608,16 @@ module Capybara
560
608
  # +evaluate_script+ whenever possible.
561
609
  #
562
610
  # @param [String] script A string of JavaScript to execute
611
+ # @param args Optional arguments that will be passed to the script. Driver support for this is optional and types of objects supported may differ between drivers
563
612
  #
564
- def execute_script(script)
613
+ def execute_script(script, *args)
565
614
  @touched = true
566
- driver.execute_script(script)
615
+ if driver.method(:execute_script).arity == 1
616
+ raise Capybara::NotSupportedByDriverError, "The current driver does not support arguments being passed with execute_script" unless args.empty?
617
+ driver.execute_script(script)
618
+ else
619
+ driver.execute_script(script, *args.map { |arg| arg.is_a?(Capybara::Node::Element) ? arg.base : arg} )
620
+ end
567
621
  end
568
622
 
569
623
  ##
@@ -575,9 +629,14 @@ module Capybara
575
629
  # @param [String] script A string of JavaScript to evaluate
576
630
  # @return [Object] The result of the evaluated JavaScript (may be driver specific)
577
631
  #
578
- def evaluate_script(script)
632
+ def evaluate_script(script, *args)
579
633
  @touched = true
580
- driver.evaluate_script(script)
634
+ if driver.method(:evaluate_script).arity == 1
635
+ raise Capybara::NotSupportedByDriverError, "The current driver does not support arguments being passed with execute_script" unless args.empty?
636
+ driver.evaluate_script(script)
637
+ else
638
+ driver.evaluate_script(script, *args.map { |arg| arg.is_a?(Capybara::Node::Element) ? arg.base : arg} )
639
+ end
581
640
  end
582
641
 
583
642
  ##
@@ -733,7 +792,9 @@ module Capybara
733
792
  end
734
793
 
735
794
  def current_scope
736
- scopes.last || document
795
+ scope = scopes.last
796
+ scope = document if [nil, :frame].include? scope
797
+ scope
737
798
  end
738
799
 
739
800
  private
@@ -19,6 +19,7 @@ Capybara::SpecHelper.spec '#accept_prompt', requires: [:modals] do
19
19
  end
20
20
 
21
21
  it "should accept the prompt with a response" do
22
+ pending "selenium-webdriver/geckodriver doesn't support sending a response to prompts" if marionette?(@session)
22
23
  @session.accept_prompt with: 'the response' do
23
24
  @session.click_link('Open prompt')
24
25
  end
@@ -26,6 +27,7 @@ Capybara::SpecHelper.spec '#accept_prompt', requires: [:modals] do
26
27
  end
27
28
 
28
29
  it "should accept the prompt if the message matches" do
30
+ pending "selenium-webdriver/geckodriver doesn't support sending a response to prompts" if marionette?(@session)
29
31
  @session.accept_prompt 'Prompt opened', with: 'matched' do
30
32
  @session.click_link('Open prompt')
31
33
  end
@@ -42,6 +44,7 @@ Capybara::SpecHelper.spec '#accept_prompt', requires: [:modals] do
42
44
 
43
45
 
44
46
  it "should return the message presented" do
47
+ pending "selenium-webdriver/geckodriver doesn't support sending a response to prompts" if marionette?(@session)
45
48
  message = @session.accept_prompt with: 'the response' do
46
49
  @session.click_link('Open prompt')
47
50
  end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+ Capybara::SpecHelper.spec '#assert_all_of_selectors' do
3
+ before do
4
+ @session.visit('/with_html')
5
+ end
6
+
7
+ it "should be true if the given selectors are on the page" do
8
+ @session.assert_all_of_selectors(:css, "p a#foo", "h2#h2one", "h2#h2two" )
9
+ end
10
+
11
+ it "should be false if any of the given selectors are not on the page" do
12
+ expect { @session.assert_all_of_selectors(:css, "p a#foo", "h2#h2three", "h2#h2one")}.to raise_error(Capybara::ElementNotFound)
13
+ end
14
+
15
+ it "should use default selector" do
16
+ Capybara.default_selector = :css
17
+ expect { @session.assert_all_of_selectors("p a#foo", "h2#h2three", "h2#h2one")}.to raise_error(Capybara::ElementNotFound)
18
+ @session.assert_all_of_selectors("p a#foo", "h2#h2two", "h2#h2one" )
19
+ end
20
+
21
+ it "should respect scopes" do
22
+ @session.within "//p[@id='first']" do
23
+ @session.assert_all_of_selectors(".//a[@id='foo']")
24
+ expect { @session.assert_all_of_selectors(".//a[@id='red']") }.to raise_error(Capybara::ElementNotFound)
25
+ end
26
+ end
27
+
28
+ context "with options" do
29
+ it "should apply options to all locators" do
30
+ @session.assert_all_of_selectors(:field, 'normal', 'additional_newline', type: :textarea)
31
+ expect { @session.assert_all_of_selectors(:field, 'normal', 'test_field', 'additional_newline', type: :textarea) }.to raise_error(Capybara::ElementNotFound)
32
+ end
33
+ end
34
+
35
+ context "with wait", requires: [:js] do
36
+ it "should not raise error if all the elements appear before given wait duration" do
37
+ Capybara.using_wait_time(0.1) do
38
+ @session.visit('/with_js')
39
+ @session.click_link('Click me')
40
+ @session.assert_all_of_selectors(:css, "a#clickable", "a#has-been-clicked", '#drag', wait: 0.9)
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ Capybara::SpecHelper.spec '#assert_none_of_selectors' do
47
+ before do
48
+ @session.visit('/with_html')
49
+ end
50
+
51
+ it "should be false if any of the given locators are on the page" do
52
+ expect { @session.assert_none_of_selectors(:xpath, "//p", "//a") }.to raise_error(Capybara::ElementNotFound)
53
+ expect { @session.assert_none_of_selectors(:xpath, "//abbr", "//a") }.to raise_error(Capybara::ElementNotFound)
54
+ expect { @session.assert_none_of_selectors(:css, "p a#foo") }.to raise_error(Capybara::ElementNotFound)
55
+ end
56
+
57
+ it "should be true if none of the given locators are on the page" do
58
+ @session.assert_none_of_selectors(:xpath, "//abbr", "//td" )
59
+ @session.assert_none_of_selectors(:css, "p a#doesnotexist", "abbr")
60
+ end
61
+
62
+ it "should use default selector" do
63
+ Capybara.default_selector = :css
64
+ @session.assert_none_of_selectors("p a#doesnotexist", "abbr")
65
+ expect { @session.assert_none_of_selectors("abbr", "p a#foo") }.to raise_error(Capybara::ElementNotFound)
66
+ end
67
+
68
+ it "should respect scopes" do
69
+ @session.within "//p[@id='first']" do
70
+ expect { @session.assert_none_of_selectors(".//a[@id='foo']") }.to raise_error(Capybara::ElementNotFound)
71
+ @session.assert_none_of_selectors(".//a[@id='red']")
72
+ end
73
+ end
74
+
75
+ context "with options" do
76
+ it "should apply the options to all locators" do
77
+ expect { @session.assert_none_of_selectors("//p//a", text: "Redirect") }.to raise_error(Capybara::ElementNotFound)
78
+ @session.assert_none_of_selectors("//p", text: "Doesnotexist")
79
+ end
80
+
81
+ it "should discard all matches where the given regexp is matched" do
82
+ expect { @session.assert_none_of_selectors("//p//a", text: /re[dab]i/i, count: 1) }.to raise_error(Capybara::ElementNotFound)
83
+ @session.assert_none_of_selectors("//p//a", text: /Red$/)
84
+ end
85
+ end
86
+
87
+ context "with wait", requires: [:js] do
88
+ it "should not find elements if they appear after given wait duration" do
89
+ @session.visit('/with_js')
90
+ @session.click_link('Click me')
91
+ @session.assert_none_of_selectors(:css, "#new_field", "a#has-been-clicked", wait: 0.1)
92
+ end
93
+ end
94
+ end