capybara 2.6.2 → 2.7.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 (163) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +40 -15
  3. data/README.md +45 -38
  4. data/lib/capybara.rb +86 -10
  5. data/lib/capybara/cucumber.rb +1 -0
  6. data/lib/capybara/driver/base.rb +5 -2
  7. data/lib/capybara/driver/node.rb +1 -0
  8. data/lib/capybara/dsl.rb +1 -0
  9. data/lib/capybara/helpers.rb +3 -2
  10. data/lib/capybara/node/actions.rb +6 -0
  11. data/lib/capybara/node/base.rb +1 -0
  12. data/lib/capybara/node/document.rb +1 -0
  13. data/lib/capybara/node/document_matchers.rb +1 -0
  14. data/lib/capybara/node/element.rb +1 -0
  15. data/lib/capybara/node/finders.rb +3 -2
  16. data/lib/capybara/node/matchers.rb +72 -2
  17. data/lib/capybara/node/simple.rb +6 -3
  18. data/lib/capybara/queries/base_query.rb +1 -0
  19. data/lib/capybara/queries/current_path_query.rb +1 -0
  20. data/lib/capybara/queries/match_query.rb +21 -0
  21. data/lib/capybara/queries/selector_query.rb +138 -0
  22. data/lib/capybara/queries/text_query.rb +1 -0
  23. data/lib/capybara/queries/title_query.rb +1 -0
  24. data/lib/capybara/query.rb +3 -131
  25. data/lib/capybara/rack_test/browser.rb +1 -0
  26. data/lib/capybara/rack_test/css_handlers.rb +1 -0
  27. data/lib/capybara/rack_test/driver.rb +6 -4
  28. data/lib/capybara/rack_test/form.rb +1 -0
  29. data/lib/capybara/rack_test/node.rb +1 -0
  30. data/lib/capybara/rails.rb +2 -1
  31. data/lib/capybara/result.rb +1 -0
  32. data/lib/capybara/rspec.rb +1 -0
  33. data/lib/capybara/rspec/features.rb +1 -0
  34. data/lib/capybara/rspec/matchers.rb +54 -3
  35. data/lib/capybara/selector.rb +175 -75
  36. data/lib/capybara/selector/filter.rb +48 -0
  37. data/lib/capybara/selenium/driver.rb +24 -7
  38. data/lib/capybara/selenium/node.rb +1 -0
  39. data/lib/capybara/server.rb +38 -6
  40. data/lib/capybara/session.rb +30 -14
  41. data/lib/capybara/session/matchers.rb +2 -1
  42. data/lib/capybara/spec/public/test.js +6 -0
  43. data/lib/capybara/spec/session/accept_alert_spec.rb +2 -1
  44. data/lib/capybara/spec/session/accept_confirm_spec.rb +2 -1
  45. data/lib/capybara/spec/session/accept_prompt_spec.rb +2 -1
  46. data/lib/capybara/spec/session/all_spec.rb +1 -0
  47. data/lib/capybara/spec/session/assert_current_path.rb +1 -0
  48. data/lib/capybara/spec/session/assert_selector.rb +1 -0
  49. data/lib/capybara/spec/session/assert_text.rb +1 -0
  50. data/lib/capybara/spec/session/assert_title.rb +1 -0
  51. data/lib/capybara/spec/session/attach_file_spec.rb +1 -0
  52. data/lib/capybara/spec/session/body_spec.rb +1 -0
  53. data/lib/capybara/spec/session/check_spec.rb +1 -0
  54. data/lib/capybara/spec/session/choose_spec.rb +1 -0
  55. data/lib/capybara/spec/session/click_button_spec.rb +1 -0
  56. data/lib/capybara/spec/session/click_link_or_button_spec.rb +1 -0
  57. data/lib/capybara/spec/session/click_link_spec.rb +1 -0
  58. data/lib/capybara/spec/session/current_scope_spec.rb +2 -1
  59. data/lib/capybara/spec/session/current_url_spec.rb +1 -0
  60. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +2 -1
  61. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +2 -1
  62. data/lib/capybara/spec/session/element/assert_match_selector.rb +31 -0
  63. data/lib/capybara/spec/session/element/match_css_spec.rb +17 -0
  64. data/lib/capybara/spec/session/element/match_xpath_spec.rb +23 -0
  65. data/lib/capybara/spec/session/element/matches_selector_spec.rb +63 -0
  66. data/lib/capybara/spec/session/evaluate_script_spec.rb +1 -0
  67. data/lib/capybara/spec/session/execute_script_spec.rb +1 -0
  68. data/lib/capybara/spec/session/fill_in_spec.rb +1 -0
  69. data/lib/capybara/spec/session/find_button_spec.rb +1 -0
  70. data/lib/capybara/spec/session/find_by_id_spec.rb +1 -0
  71. data/lib/capybara/spec/session/find_field_spec.rb +1 -0
  72. data/lib/capybara/spec/session/find_link_spec.rb +1 -0
  73. data/lib/capybara/spec/session/find_spec.rb +1 -0
  74. data/lib/capybara/spec/session/first_spec.rb +1 -0
  75. data/lib/capybara/spec/session/go_back_spec.rb +1 -0
  76. data/lib/capybara/spec/session/go_forward_spec.rb +1 -0
  77. data/lib/capybara/spec/session/has_button_spec.rb +1 -0
  78. data/lib/capybara/spec/session/has_css_spec.rb +1 -0
  79. data/lib/capybara/spec/session/has_current_path_spec.rb +1 -0
  80. data/lib/capybara/spec/session/has_field_spec.rb +13 -0
  81. data/lib/capybara/spec/session/has_link_spec.rb +1 -0
  82. data/lib/capybara/spec/session/has_select_spec.rb +19 -0
  83. data/lib/capybara/spec/session/has_selector_spec.rb +6 -0
  84. data/lib/capybara/spec/session/has_table_spec.rb +1 -0
  85. data/lib/capybara/spec/session/has_text_spec.rb +1 -0
  86. data/lib/capybara/spec/session/has_title_spec.rb +1 -0
  87. data/lib/capybara/spec/session/has_xpath_spec.rb +1 -0
  88. data/lib/capybara/spec/session/headers.rb +1 -0
  89. data/lib/capybara/spec/session/html_spec.rb +1 -0
  90. data/lib/capybara/spec/session/node_spec.rb +29 -0
  91. data/lib/capybara/spec/session/reset_session_spec.rb +35 -1
  92. data/lib/capybara/spec/session/response_code.rb +1 -0
  93. data/lib/capybara/spec/session/save_and_open_page_spec.rb +1 -0
  94. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +3 -2
  95. data/lib/capybara/spec/session/save_page_spec.rb +34 -2
  96. data/lib/capybara/spec/session/save_screenshot_spec.rb +31 -1
  97. data/lib/capybara/spec/session/screenshot_spec.rb +3 -1
  98. data/lib/capybara/spec/session/select_spec.rb +1 -0
  99. data/lib/capybara/spec/session/selectors_spec.rb +35 -0
  100. data/lib/capybara/spec/session/text_spec.rb +1 -0
  101. data/lib/capybara/spec/session/title_spec.rb +2 -1
  102. data/lib/capybara/spec/session/uncheck_spec.rb +1 -0
  103. data/lib/capybara/spec/session/unselect_spec.rb +1 -0
  104. data/lib/capybara/spec/session/visit_spec.rb +1 -0
  105. data/lib/capybara/spec/session/window/become_closed_spec.rb +1 -0
  106. data/lib/capybara/spec/session/window/current_window_spec.rb +1 -0
  107. data/lib/capybara/spec/session/window/open_new_window_spec.rb +1 -0
  108. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +1 -0
  109. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +1 -0
  110. data/lib/capybara/spec/session/window/window_spec.rb +1 -8
  111. data/lib/capybara/spec/session/window/windows_spec.rb +1 -0
  112. data/lib/capybara/spec/session/window/within_window_spec.rb +2 -3
  113. data/lib/capybara/spec/session/within_frame_spec.rb +1 -0
  114. data/lib/capybara/spec/session/within_spec.rb +1 -0
  115. data/lib/capybara/spec/spec_helper.rb +2 -0
  116. data/lib/capybara/spec/test_app.rb +1 -0
  117. data/lib/capybara/spec/views/buttons.erb +1 -0
  118. data/lib/capybara/spec/views/fieldsets.erb +2 -1
  119. data/lib/capybara/spec/views/form.erb +24 -0
  120. data/lib/capybara/spec/views/frame_child.erb +2 -1
  121. data/lib/capybara/spec/views/frame_one.erb +2 -1
  122. data/lib/capybara/spec/views/frame_parent.erb +2 -1
  123. data/lib/capybara/spec/views/frame_two.erb +2 -1
  124. data/lib/capybara/spec/views/header_links.erb +1 -0
  125. data/lib/capybara/spec/views/host_links.erb +1 -0
  126. data/lib/capybara/spec/views/path.erb +1 -0
  127. data/lib/capybara/spec/views/popup_one.erb +1 -0
  128. data/lib/capybara/spec/views/popup_two.erb +1 -0
  129. data/lib/capybara/spec/views/postback.erb +2 -1
  130. data/lib/capybara/spec/views/tables.erb +1 -0
  131. data/lib/capybara/spec/views/with_base_tag.erb +1 -0
  132. data/lib/capybara/spec/views/with_count.erb +2 -1
  133. data/lib/capybara/spec/views/with_hover.erb +2 -1
  134. data/lib/capybara/spec/views/with_html.erb +3 -0
  135. data/lib/capybara/spec/views/with_html_entities.erb +1 -0
  136. data/lib/capybara/spec/views/with_js.erb +5 -0
  137. data/lib/capybara/spec/views/with_scope.erb +1 -0
  138. data/lib/capybara/spec/views/with_simple_html.erb +2 -1
  139. data/lib/capybara/spec/views/with_slow_unload.erb +17 -0
  140. data/lib/capybara/spec/views/with_title.erb +2 -1
  141. data/lib/capybara/spec/views/with_unload_alert.erb +12 -0
  142. data/lib/capybara/spec/views/with_windows.erb +1 -0
  143. data/lib/capybara/spec/views/within_frames.erb +2 -1
  144. data/lib/capybara/version.rb +2 -1
  145. data/lib/capybara/window.rb +1 -0
  146. data/spec/basic_node_spec.rb +1 -0
  147. data/spec/capybara_spec.rb +28 -1
  148. data/spec/dsl_spec.rb +1 -0
  149. data/spec/fixtures/selenium_driver_rspec_failure.rb +1 -0
  150. data/spec/fixtures/selenium_driver_rspec_success.rb +1 -0
  151. data/spec/rack_test_spec.rb +1 -0
  152. data/spec/result_spec.rb +1 -0
  153. data/spec/rspec/features_spec.rb +1 -0
  154. data/spec/rspec/matchers_spec.rb +1 -0
  155. data/spec/rspec/scenarios_spec.rb +1 -0
  156. data/spec/rspec/views_spec.rb +1 -0
  157. data/spec/rspec_spec.rb +1 -0
  158. data/spec/selector_spec.rb +55 -1
  159. data/spec/selenium_spec.rb +6 -2
  160. data/spec/selenium_spec_chrome.rb +1 -0
  161. data/spec/server_spec.rb +101 -13
  162. data/spec/spec_helper.rb +1 -0
  163. metadata +16 -6
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'capybara/dsl'
2
3
  require 'capybara/rspec/matchers'
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  class Capybara::Driver::Base
2
3
  def current_url
3
4
  raise NotImplementedError
@@ -90,8 +91,8 @@ class Capybara::Driver::Base
90
91
  def no_such_window_error
91
92
  raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#no_such_window_error'
92
93
  end
93
-
94
-
94
+
95
+
95
96
  ##
96
97
  #
97
98
  # Execute the block, and then accept the modal opened.
@@ -134,7 +135,9 @@ class Capybara::Driver::Base
134
135
  false
135
136
  end
136
137
 
138
+ # @deprecated This method is being removed
137
139
  def browser_initialized?
140
+ warn "DEPRECATED: #browser_initialized? is deprecated and will be removed in the next version of Capybara"
138
141
  true
139
142
  end
140
143
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Capybara
2
3
  module Driver
3
4
  class Node
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'capybara'
2
3
 
3
4
  module Capybara
@@ -1,4 +1,5 @@
1
1
  # encoding: UTF-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module Capybara
4
5
 
@@ -96,7 +97,7 @@ module Capybara
96
97
  # @option [Integer] minimum Count should have been larger than or equal to this value
97
98
  #
98
99
  def failure_message(description, options={})
99
- message = "expected to find #{description}"
100
+ message = String.new("expected to find #{description}")
100
101
  if options[:count]
101
102
  message << " #{options[:count]} #{declension('time', 'times', options[:count])}"
102
103
  elsif options[:between]
@@ -129,7 +130,7 @@ module Capybara
129
130
 
130
131
  if defined?(Process::CLOCK_MONOTONIC)
131
132
  def monotonic_time
132
- Process.clock_gettime Process::CLOCK_MONOTONIC
133
+ Process.clock_gettime Process::CLOCK_MONOTONIC
133
134
  end
134
135
  else
135
136
  def monotonic_time
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Capybara
2
3
  module Node
3
4
  module Actions
@@ -150,6 +151,11 @@ module Capybara
150
151
  # @param [String] locator Which field to attach the file to
151
152
  # @param [String] path The path of the file that will be attached, or an array of paths
152
153
  #
154
+ # @option options [Symbol] match (Capybara.match) The matching strategy to use (:one, :first, :prefer_exact, :smart).
155
+ # @option options [Boolean] exact (Capybara.exact) Match the exact label name/contents or accept a partial match.
156
+ # @option options [Fixnum] wait (Capybara.default_max_wait_time) If using a Javascript driver, maximum number of seconds during which the element will be searched for.
157
+ # @option options [Boolean] multiple Match field which allows multiple file selection
158
+ #
153
159
  def attach_file(locator, path, options={})
154
160
  Array(path).each do |p|
155
161
  raise Capybara::FileNotFound, "cannot attach file, #{p} does not exist" unless File.exist?(p.to_s)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Capybara
2
3
  module Node
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Capybara
2
3
  module Node
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Capybara
2
3
  module Node
3
4
  module DocumentMatchers
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Capybara
2
3
  module Node
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Capybara
2
3
  module Node
3
4
  module Finders
@@ -28,7 +29,7 @@ module Capybara
28
29
  # @raise [Capybara::ElementNotFound] If the element can't be found before time expires
29
30
  #
30
31
  def find(*args)
31
- query = Capybara::Query.new(*args)
32
+ query = Capybara::Queries::SelectorQuery.new(*args)
32
33
  synchronize(query.wait) do
33
34
  if query.match == :smart or query.match == :prefer_exact
34
35
  result = query.resolve_for(self, true)
@@ -177,7 +178,7 @@ module Capybara
177
178
  # @return [Capybara::Result] A collection of found elements
178
179
  #
179
180
  def all(*args)
180
- query = Capybara::Query.new(*args)
181
+ query = Capybara::Queries::SelectorQuery.new(*args)
181
182
  synchronize(query.wait) do
182
183
  result = query.resolve_for(self)
183
184
  raise Capybara::ExpectationNotMet, result.failure_message unless result.matches_count?
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Capybara
2
3
  module Node
3
4
  module Matchers
@@ -55,6 +56,36 @@ module Capybara
55
56
  return false
56
57
  end
57
58
 
59
+ ##
60
+ #
61
+ # Checks if the current node matches given selector
62
+ # Usage is identical to Capybara::Node::Matchers#has_selector?
63
+ #
64
+ # @param (see Capybara::Node::Finders#has_selector?)
65
+ # @return [Boolean]
66
+ #
67
+ def matches_selector?(*args)
68
+ assert_matches_selector(*args)
69
+ rescue Capybara::ExpectationNotMet
70
+ return false
71
+ end
72
+
73
+
74
+ ##
75
+ #
76
+ # Checks if the current node does not match given selector
77
+ # Usage is identical to Capybara::Node::Matchers#has_selector?
78
+ #
79
+ # @param (see Capybara::Node::Finders#has_selector?)
80
+ # @return [Boolean]
81
+ #
82
+ def not_matches_selector?(*args)
83
+ assert_not_matches_selector(*args)
84
+ rescue Capybara::ExpectationNotMet
85
+ return false
86
+ end
87
+
88
+
58
89
  ##
59
90
  #
60
91
  # Asserts that a given selector is on the page or current node.
@@ -89,7 +120,7 @@ module Capybara
89
120
  # @raise [Capybara::ExpectationNotMet] If the selector does not exist
90
121
  #
91
122
  def assert_selector(*args)
92
- query = Capybara::Query.new(*args)
123
+ query = Capybara::Queries::SelectorQuery.new(*args)
93
124
  synchronize(query.wait) do
94
125
  result = query.resolve_for(self)
95
126
  matches_count = Capybara::Helpers.matches_count?(result.size, query.options)
@@ -117,7 +148,7 @@ module Capybara
117
148
  # @raise [Capybara::ExpectationNotMet] If the selector exists
118
149
  #
119
150
  def assert_no_selector(*args)
120
- query = Capybara::Query.new(*args)
151
+ query = Capybara::Queries::SelectorQuery.new(*args)
121
152
  synchronize(query.wait) do
122
153
  result = query.resolve_for(self)
123
154
  matches_count = Capybara::Helpers.matches_count?(result.size, query.options)
@@ -129,6 +160,45 @@ module Capybara
129
160
  end
130
161
  alias_method :refute_selector, :assert_no_selector
131
162
 
163
+ ##
164
+ #
165
+ # Asserts that the current_node matches a given selector
166
+ #
167
+ # node.assert_matches_selector('p#foo')
168
+ # node.assert_matches_selector(:xpath, '//p[@id="foo"]')
169
+ # node.assert_matches_selector(:foo)
170
+ #
171
+ # It also accepts all options that {Capybara::Node::Finders#all} accepts,
172
+ # such as :text and :visible.
173
+ #
174
+ # node.assert_matches_selector('li', :text => 'Horse', :visible => true)
175
+ #
176
+ # @param (see Capybara::Node::Finders#all)
177
+ # @raise [Capybara::ExpectationNotMet] If the selector does not match
178
+ #
179
+ def assert_matches_selector(*args)
180
+ query = Capybara::Queries::MatchQuery.new(*args)
181
+ synchronize(query.wait) do
182
+ result = query.resolve_for(self.parent)
183
+ unless result.include? self
184
+ raise Capybara::ExpectationNotMet, "Item does not match the provided selector"
185
+ end
186
+ end
187
+ return true
188
+ end
189
+
190
+ def assert_not_matches_selector(*args)
191
+ query = Capybara::Queries::MatchQuery.new(*args)
192
+ synchronize(query.wait) do
193
+ result = query.resolve_for(self.parent)
194
+ if result.include? self
195
+ raise Capybara::ExpectationNotMet, 'Item matched the provided selector'
196
+ end
197
+ end
198
+ return true
199
+ end
200
+ alias_method :refute_matches_selector, :assert_not_matches_selector
201
+
132
202
  ##
133
203
  #
134
204
  # Checks if a given XPath expression is on the page or current node.
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Capybara
2
3
  module Node
3
4
 
@@ -99,6 +100,8 @@ module Capybara
99
100
  # @return [Boolean] Whether the element is visible
100
101
  #
101
102
  def visible?(check_ancestors = true)
103
+ return false if (tag_name == 'input') && (native[:type]=="hidden")
104
+
102
105
  if check_ancestors
103
106
  #check size because oldest supported nokogiri doesnt support xpath boolean() function
104
107
  native.xpath("./ancestor-or-self::*[contains(@style, 'display:none') or contains(@style, 'display: none') or @hidden or name()='script' or name()='head']").size() == 0
@@ -115,7 +118,7 @@ module Capybara
115
118
  # @return [Boolean] Whether the element is checked
116
119
  #
117
120
  def checked?
118
- native[:checked]
121
+ native.has_attribute?('checked')
119
122
  end
120
123
 
121
124
  ##
@@ -124,7 +127,7 @@ module Capybara
124
127
  #
125
128
  # @return [Boolean] Whether the element is disabled
126
129
  def disabled?
127
- native[:disabled]
130
+ native.has_attribute?('disabled')
128
131
  end
129
132
 
130
133
  ##
@@ -134,7 +137,7 @@ module Capybara
134
137
  # @return [Boolean] Whether the element is selected
135
138
  #
136
139
  def selected?
137
- native[:selected]
140
+ native.has_attribute?('selected')
138
141
  end
139
142
 
140
143
  def synchronize(seconds=nil)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Capybara
2
3
  # @api private
3
4
  module Queries
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'addressable/uri'
2
3
 
3
4
  module Capybara
@@ -0,0 +1,21 @@
1
+ module Capybara
2
+ module Queries
3
+ class MatchQuery < Capybara::Queries::SelectorQuery
4
+ VALID_KEYS = [:text, :visible, :exact, :wait]
5
+
6
+ def visible
7
+ if options.has_key?(:visible)
8
+ super
9
+ else
10
+ :all
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def valid_keys
17
+ VALID_KEYS + @selector.custom_filters.keys
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+ module Capybara
3
+ module Queries
4
+ class SelectorQuery < Queries::BaseQuery
5
+ attr_accessor :selector, :locator, :options, :expression, :find, :negative
6
+
7
+ VALID_KEYS = [:text, :visible, :between, :count, :maximum, :minimum, :exact, :match, :wait]
8
+ VALID_MATCH = [:first, :smart, :prefer_exact, :one]
9
+
10
+ def initialize(*args)
11
+ @options = if args.last.is_a?(Hash) then args.pop.dup else {} end
12
+
13
+ if args[0].is_a?(Symbol)
14
+ @selector = Selector.all[args.shift]
15
+ @locator = args.shift
16
+ else
17
+ @selector = Selector.all.values.find { |s| s.match?(args[0]) }
18
+ @locator = args.shift
19
+ end
20
+ @selector ||= Selector.all[Capybara.default_selector]
21
+
22
+ warn "Unused parameters passed to #{self.class.name} : #{args.to_s}" unless args.empty?
23
+
24
+ # for compatibility with Capybara 2.0
25
+ if Capybara.exact_options and @selector == Selector.all[:option]
26
+ @options[:exact] = true
27
+ end
28
+
29
+ @expression = @selector.call(@locator)
30
+ assert_valid_keys
31
+ end
32
+
33
+ def name; selector.name; end
34
+ def label; selector.label or selector.name; end
35
+
36
+ def description
37
+ @description = String.new("#{label} #{locator.inspect}")
38
+ @description << " with text #{options[:text].inspect}" if options[:text]
39
+ @description << selector.description(options)
40
+ @description
41
+ end
42
+
43
+ def matches_filters?(node)
44
+ if options[:text]
45
+ regexp = options[:text].is_a?(Regexp) ? options[:text] : Regexp.escape(options[:text].to_s)
46
+ return false if not node.text(visible).match(regexp)
47
+ end
48
+ case visible
49
+ when :visible then return false unless node.visible?
50
+ when :hidden then return false if node.visible?
51
+ end
52
+ selector.custom_filters.each do |name, filter|
53
+ if options.has_key?(name)
54
+ return false unless filter.matches?(node, options[name])
55
+ elsif filter.default?
56
+ return false unless filter.matches?(node, filter.default)
57
+ end
58
+ end
59
+ end
60
+
61
+ def visible
62
+ if options.has_key?(:visible)
63
+ case @options[:visible]
64
+ when true then :visible
65
+ when false then :all
66
+ else @options[:visible]
67
+ end
68
+ else
69
+ if Capybara.ignore_hidden_elements
70
+ :visible
71
+ else
72
+ :all
73
+ end
74
+ end
75
+ end
76
+
77
+ def exact?
78
+ if options.has_key?(:exact)
79
+ @options[:exact]
80
+ else
81
+ Capybara.exact
82
+ end
83
+ end
84
+
85
+ def match
86
+ if options.has_key?(:match)
87
+ @options[:match]
88
+ else
89
+ Capybara.match
90
+ end
91
+ end
92
+
93
+ def xpath(exact=nil)
94
+ exact = self.exact? if exact == nil
95
+ if @expression.respond_to?(:to_xpath) and exact
96
+ @expression.to_xpath(:exact)
97
+ else
98
+ @expression.to_s
99
+ end
100
+ end
101
+
102
+ def css
103
+ @expression
104
+ end
105
+
106
+ # @api private
107
+ def resolve_for(node, exact = nil)
108
+ node.synchronize do
109
+ children = if selector.format == :css
110
+ node.find_css(self.css)
111
+ else
112
+ node.find_xpath(self.xpath(exact))
113
+ end.map do |child|
114
+ if node.is_a?(Capybara::Node::Base)
115
+ Capybara::Node::Element.new(node.session, child, node, self)
116
+ else
117
+ Capybara::Node::Simple.new(child)
118
+ end
119
+ end
120
+ Capybara::Result.new(children, self)
121
+ end
122
+ end
123
+
124
+ private
125
+
126
+ def valid_keys
127
+ COUNT_KEYS + [:text, :visible, :exact, :match, :wait] + @selector.custom_filters.keys
128
+ end
129
+
130
+ def assert_valid_keys
131
+ super
132
+ unless VALID_MATCH.include?(match)
133
+ raise ArgumentError, "invalid option #{match.inspect} for :match, should be one of #{VALID_MATCH.map(&:inspect).join(", ")}"
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end