capybara 2.11.0 → 2.12.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.
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