capybara 2.5.0 → 2.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (205) hide show
  1. checksums.yaml +5 -5
  2. data/.yard/templates_custom/default/class/html/selectors.erb +38 -0
  3. data/.yard/templates_custom/default/class/html/setup.rb +17 -0
  4. data/.yard/yard_extensions.rb +78 -0
  5. data/.yardopts +1 -0
  6. data/History.md +413 -10
  7. data/License.txt +1 -1
  8. data/README.md +237 -130
  9. data/lib/capybara/config.rb +132 -0
  10. data/lib/capybara/cucumber.rb +3 -1
  11. data/lib/capybara/driver/base.rb +27 -6
  12. data/lib/capybara/driver/node.rb +14 -5
  13. data/lib/capybara/dsl.rb +2 -3
  14. data/lib/capybara/helpers.rb +13 -65
  15. data/lib/capybara/minitest/spec.rb +177 -0
  16. data/lib/capybara/minitest.rb +278 -0
  17. data/lib/capybara/node/actions.rb +180 -24
  18. data/lib/capybara/node/base.rb +17 -5
  19. data/lib/capybara/node/document.rb +5 -0
  20. data/lib/capybara/node/document_matchers.rb +15 -14
  21. data/lib/capybara/node/element.rb +55 -7
  22. data/lib/capybara/node/finders.rb +179 -67
  23. data/lib/capybara/node/matchers.rb +301 -105
  24. data/lib/capybara/node/simple.rb +15 -4
  25. data/lib/capybara/queries/ancestor_query.rb +25 -0
  26. data/lib/capybara/queries/base_query.rb +69 -3
  27. data/lib/capybara/queries/current_path_query.rb +17 -8
  28. data/lib/capybara/queries/match_query.rb +19 -0
  29. data/lib/capybara/queries/selector_query.rb +251 -0
  30. data/lib/capybara/queries/sibling_query.rb +25 -0
  31. data/lib/capybara/queries/text_query.rb +67 -16
  32. data/lib/capybara/queries/title_query.rb +4 -2
  33. data/lib/capybara/query.rb +3 -131
  34. data/lib/capybara/rack_test/browser.rb +14 -5
  35. data/lib/capybara/rack_test/css_handlers.rb +1 -0
  36. data/lib/capybara/rack_test/driver.rb +15 -8
  37. data/lib/capybara/rack_test/form.rb +34 -12
  38. data/lib/capybara/rack_test/node.rb +29 -12
  39. data/lib/capybara/rails.rb +3 -3
  40. data/lib/capybara/result.rb +104 -9
  41. data/lib/capybara/rspec/compound.rb +95 -0
  42. data/lib/capybara/rspec/features.rb +17 -6
  43. data/lib/capybara/rspec/matcher_proxies.rb +45 -0
  44. data/lib/capybara/rspec/matchers.rb +199 -80
  45. data/lib/capybara/rspec.rb +4 -2
  46. data/lib/capybara/selector/css.rb +30 -0
  47. data/lib/capybara/selector/filter.rb +20 -0
  48. data/lib/capybara/selector/filter_set.rb +74 -0
  49. data/lib/capybara/selector/filters/base.rb +33 -0
  50. data/lib/capybara/selector/filters/expression_filter.rb +40 -0
  51. data/lib/capybara/selector/filters/node_filter.rb +27 -0
  52. data/lib/capybara/selector/selector.rb +276 -0
  53. data/lib/capybara/selector.rb +452 -157
  54. data/lib/capybara/selenium/driver.rb +282 -81
  55. data/lib/capybara/selenium/node.rb +144 -46
  56. data/lib/capybara/server.rb +59 -16
  57. data/lib/capybara/session/config.rb +114 -0
  58. data/lib/capybara/session/matchers.rb +29 -19
  59. data/lib/capybara/session.rb +378 -143
  60. data/lib/capybara/spec/fixtures/no_extension +1 -0
  61. data/lib/capybara/spec/public/jquery-ui.js +13 -791
  62. data/lib/capybara/spec/public/jquery.js +4 -9045
  63. data/lib/capybara/spec/public/test.js +45 -11
  64. data/lib/capybara/spec/session/accept_alert_spec.rb +30 -7
  65. data/lib/capybara/spec/session/accept_confirm_spec.rb +14 -2
  66. data/lib/capybara/spec/session/accept_prompt_spec.rb +35 -6
  67. data/lib/capybara/spec/session/all_spec.rb +45 -32
  68. data/lib/capybara/spec/session/ancestor_spec.rb +85 -0
  69. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +110 -0
  70. data/lib/capybara/spec/session/assert_current_path.rb +15 -2
  71. data/lib/capybara/spec/session/assert_selector.rb +29 -28
  72. data/lib/capybara/spec/session/assert_text.rb +59 -20
  73. data/lib/capybara/spec/session/assert_title.rb +25 -11
  74. data/lib/capybara/spec/session/attach_file_spec.rb +42 -4
  75. data/lib/capybara/spec/session/body_spec.rb +1 -0
  76. data/lib/capybara/spec/session/check_spec.rb +90 -14
  77. data/lib/capybara/spec/session/choose_spec.rb +31 -5
  78. data/lib/capybara/spec/session/click_button_spec.rb +20 -9
  79. data/lib/capybara/spec/session/click_link_or_button_spec.rb +15 -9
  80. data/lib/capybara/spec/session/click_link_spec.rb +39 -15
  81. data/lib/capybara/spec/session/current_scope_spec.rb +2 -1
  82. data/lib/capybara/spec/session/current_url_spec.rb +12 -3
  83. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +6 -5
  84. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +4 -3
  85. data/lib/capybara/spec/session/element/assert_match_selector.rb +36 -0
  86. data/lib/capybara/spec/session/element/match_css_spec.rb +23 -0
  87. data/lib/capybara/spec/session/element/match_xpath_spec.rb +23 -0
  88. data/lib/capybara/spec/session/element/matches_selector_spec.rb +106 -0
  89. data/lib/capybara/spec/session/evaluate_async_script_spec.rb +22 -0
  90. data/lib/capybara/spec/session/evaluate_script_spec.rb +23 -1
  91. data/lib/capybara/spec/session/execute_script_spec.rb +22 -3
  92. data/lib/capybara/spec/session/fill_in_spec.rb +50 -32
  93. data/lib/capybara/spec/session/find_button_spec.rb +43 -2
  94. data/lib/capybara/spec/session/find_by_id_spec.rb +3 -2
  95. data/lib/capybara/spec/session/find_field_spec.rb +42 -6
  96. data/lib/capybara/spec/session/find_link_spec.rb +22 -3
  97. data/lib/capybara/spec/session/find_spec.rb +103 -57
  98. data/lib/capybara/spec/session/first_spec.rb +34 -18
  99. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +103 -0
  100. data/lib/capybara/spec/session/{within_frame_spec.rb → frame/within_frame_spec.rb} +44 -2
  101. data/lib/capybara/spec/session/go_back_spec.rb +2 -1
  102. data/lib/capybara/spec/session/go_forward_spec.rb +2 -1
  103. data/lib/capybara/spec/session/has_all_selectors_spec.rb +69 -0
  104. data/lib/capybara/spec/session/has_button_spec.rb +17 -8
  105. data/lib/capybara/spec/session/has_css_spec.rb +85 -73
  106. data/lib/capybara/spec/session/has_current_path_spec.rb +91 -7
  107. data/lib/capybara/spec/session/has_field_spec.rb +93 -58
  108. data/lib/capybara/spec/session/has_link_spec.rb +9 -8
  109. data/lib/capybara/spec/session/has_none_selectors_spec.rb +76 -0
  110. data/lib/capybara/spec/session/has_select_spec.rb +159 -59
  111. data/lib/capybara/spec/session/has_selector_spec.rb +64 -28
  112. data/lib/capybara/spec/session/has_table_spec.rb +1 -0
  113. data/lib/capybara/spec/session/has_text_spec.rb +27 -12
  114. data/lib/capybara/spec/session/has_title_spec.rb +22 -4
  115. data/lib/capybara/spec/session/has_xpath_spec.rb +32 -29
  116. data/lib/capybara/spec/session/headers.rb +2 -1
  117. data/lib/capybara/spec/session/html_spec.rb +4 -3
  118. data/lib/capybara/spec/session/node_spec.rb +198 -38
  119. data/lib/capybara/spec/session/refresh_spec.rb +28 -0
  120. data/lib/capybara/spec/session/reset_session_spec.rb +46 -5
  121. data/lib/capybara/spec/session/response_code.rb +2 -1
  122. data/lib/capybara/spec/session/save_and_open_page_spec.rb +1 -0
  123. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +6 -5
  124. data/lib/capybara/spec/session/save_page_spec.rb +34 -2
  125. data/lib/capybara/spec/session/save_screenshot_spec.rb +31 -1
  126. data/lib/capybara/spec/session/screenshot_spec.rb +4 -2
  127. data/lib/capybara/spec/session/select_spec.rb +34 -32
  128. data/lib/capybara/spec/session/selectors_spec.rb +65 -0
  129. data/lib/capybara/spec/session/sibling_spec.rb +52 -0
  130. data/lib/capybara/spec/session/text_spec.rb +4 -4
  131. data/lib/capybara/spec/session/title_spec.rb +2 -1
  132. data/lib/capybara/spec/session/uncheck_spec.rb +42 -2
  133. data/lib/capybara/spec/session/unselect_spec.rb +17 -16
  134. data/lib/capybara/spec/session/visit_spec.rb +77 -2
  135. data/lib/capybara/spec/session/window/become_closed_spec.rb +12 -11
  136. data/lib/capybara/spec/session/window/current_window_spec.rb +1 -0
  137. data/lib/capybara/spec/session/window/open_new_window_spec.rb +1 -0
  138. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +16 -11
  139. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +7 -4
  140. data/lib/capybara/spec/session/window/window_spec.rb +36 -29
  141. data/lib/capybara/spec/session/window/windows_spec.rb +1 -0
  142. data/lib/capybara/spec/session/window/within_window_spec.rb +31 -7
  143. data/lib/capybara/spec/session/within_spec.rb +14 -6
  144. data/lib/capybara/spec/spec_helper.rb +37 -4
  145. data/lib/capybara/spec/test_app.rb +15 -3
  146. data/lib/capybara/spec/views/buttons.erb +1 -0
  147. data/lib/capybara/spec/views/fieldsets.erb +2 -1
  148. data/lib/capybara/spec/views/form.erb +169 -9
  149. data/lib/capybara/spec/views/frame_child.erb +10 -2
  150. data/lib/capybara/spec/views/frame_one.erb +2 -1
  151. data/lib/capybara/spec/views/frame_parent.erb +3 -2
  152. data/lib/capybara/spec/views/frame_two.erb +2 -1
  153. data/lib/capybara/spec/views/header_links.erb +1 -0
  154. data/lib/capybara/spec/views/host_links.erb +1 -0
  155. data/lib/capybara/spec/views/initial_alert.erb +10 -0
  156. data/lib/capybara/spec/views/path.erb +1 -0
  157. data/lib/capybara/spec/views/popup_one.erb +1 -0
  158. data/lib/capybara/spec/views/popup_two.erb +1 -0
  159. data/lib/capybara/spec/views/postback.erb +2 -1
  160. data/lib/capybara/spec/views/tables.erb +1 -0
  161. data/lib/capybara/spec/views/with_base_tag.erb +1 -0
  162. data/lib/capybara/spec/views/with_count.erb +2 -1
  163. data/lib/capybara/spec/views/with_fixed_header_footer.erb +17 -0
  164. data/lib/capybara/spec/views/with_hover.erb +7 -1
  165. data/lib/capybara/spec/views/with_html.erb +40 -2
  166. data/lib/capybara/spec/views/with_html_entities.erb +1 -0
  167. data/lib/capybara/spec/views/with_js.erb +32 -1
  168. data/lib/capybara/spec/views/with_scope.erb +1 -0
  169. data/lib/capybara/spec/views/with_simple_html.erb +2 -1
  170. data/lib/capybara/spec/views/with_slow_unload.erb +17 -0
  171. data/lib/capybara/spec/views/with_title.erb +2 -1
  172. data/lib/capybara/spec/views/with_unload_alert.erb +14 -0
  173. data/lib/capybara/spec/views/with_windows.erb +7 -0
  174. data/lib/capybara/spec/views/within_frames.erb +3 -2
  175. data/lib/capybara/version.rb +2 -1
  176. data/lib/capybara/window.rb +20 -3
  177. data/lib/capybara.rb +189 -93
  178. data/spec/basic_node_spec.rb +7 -6
  179. data/spec/capybara_spec.rb +90 -4
  180. data/spec/dsl_spec.rb +3 -1
  181. data/spec/filter_set_spec.rb +28 -0
  182. data/spec/fixtures/capybara.csv +1 -0
  183. data/spec/fixtures/selenium_driver_rspec_failure.rb +5 -1
  184. data/spec/fixtures/selenium_driver_rspec_success.rb +5 -1
  185. data/spec/minitest_spec.rb +130 -0
  186. data/spec/minitest_spec_spec.rb +135 -0
  187. data/spec/per_session_config_spec.rb +67 -0
  188. data/spec/rack_test_spec.rb +50 -7
  189. data/spec/result_spec.rb +76 -0
  190. data/spec/rspec/features_spec.rb +21 -8
  191. data/spec/rspec/scenarios_spec.rb +21 -0
  192. data/spec/rspec/{matchers_spec.rb → shared_spec_matchers.rb} +160 -54
  193. data/spec/rspec/views_spec.rb +5 -0
  194. data/spec/rspec_matchers_spec.rb +46 -0
  195. data/spec/rspec_spec.rb +79 -1
  196. data/spec/selector_spec.rb +199 -0
  197. data/spec/selenium_spec_chrome.rb +54 -9
  198. data/spec/selenium_spec_firefox.rb +68 -0
  199. data/spec/selenium_spec_marionette.rb +127 -0
  200. data/spec/server_spec.rb +102 -14
  201. data/spec/session_spec.rb +54 -0
  202. data/spec/shared_selenium_session.rb +215 -0
  203. data/spec/spec_helper.rb +7 -0
  204. metadata +140 -15
  205. data/spec/selenium_spec.rb +0 -128
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+ require 'capybara/selector/filter'
3
+
4
+ module Capybara
5
+ class Selector
6
+ class FilterSet
7
+ attr_reader :descriptions
8
+
9
+ def initialize(name, &block)
10
+ @name = name
11
+ @descriptions = []
12
+ instance_eval(&block)
13
+ end
14
+
15
+ def filter(name, *types_and_options, &block)
16
+ add_filter(name, Filters::NodeFilter, *types_and_options, &block)
17
+ end
18
+
19
+ def expression_filter(name, *types_and_options, &block)
20
+ add_filter(name, Filters::ExpressionFilter, *types_and_options, &block)
21
+ end
22
+
23
+ def describe(&block)
24
+ descriptions.push block
25
+ end
26
+
27
+ def description(options={})
28
+ options_with_defaults = options.dup
29
+ filters.each do |name, filter|
30
+ options_with_defaults[name] = filter.default if filter.default? && !options_with_defaults.has_key?(name)
31
+ end
32
+
33
+ @descriptions.map do |desc|
34
+ desc.call(options_with_defaults).to_s
35
+ end.join
36
+ end
37
+
38
+ def filters
39
+ @filters ||= {}
40
+ end
41
+
42
+ def node_filters
43
+ filters.reject { |_n, f| f.nil? || f.is_a?(Filters::ExpressionFilter) }.freeze
44
+ end
45
+
46
+ def expression_filters
47
+ filters.select { |_n, f| f.nil? || f.is_a?(Filters::ExpressionFilter) }.freeze
48
+ end
49
+
50
+ class << self
51
+
52
+ def all
53
+ @filter_sets ||= {}
54
+ end
55
+
56
+ def add(name, &block)
57
+ all[name.to_sym] = FilterSet.new(name.to_sym, &block)
58
+ end
59
+
60
+ def remove(name)
61
+ all.delete(name.to_sym)
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def add_filter(name, filter_class, *types_and_options, &block)
68
+ options = types_and_options.last.is_a?(Hash) ? types_and_options.pop.dup : {}
69
+ types_and_options.each { |k| options[k] = true}
70
+ filters[name] = filter_class.new(name, block, options)
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+ module Capybara
3
+ class Selector
4
+ module Filters
5
+ class Base
6
+ def initialize(name, block, options={})
7
+ @name = name
8
+ @block = block
9
+ @options = options
10
+ @options[:valid_values] = [true,false] if options[:boolean]
11
+ end
12
+
13
+ def default?
14
+ @options.has_key?(:default)
15
+ end
16
+
17
+ def default
18
+ @options[:default]
19
+ end
20
+
21
+ def skip?(value)
22
+ @options.has_key?(:skip_if) && value == @options[:skip_if]
23
+ end
24
+
25
+ private
26
+
27
+ def valid_value?(value)
28
+ !@options.has_key?(:valid_values) || Array(@options[:valid_values]).include?(value)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+ require 'capybara/selector/filters/base'
3
+
4
+ module Capybara
5
+ class Selector
6
+ module Filters
7
+ class ExpressionFilter < Base
8
+ def apply_filter(expr, value)
9
+ return expr if skip?(value)
10
+
11
+ if !valid_value?(value)
12
+ msg = "Invalid value #{value.inspect} passed to expression filter #{@name} - "
13
+ if default?
14
+ warn msg + "defaulting to #{default}"
15
+ value = default
16
+ else
17
+ warn msg + "skipping"
18
+ return expr
19
+ end
20
+ end
21
+
22
+ @block.call(expr, value)
23
+ end
24
+ end
25
+
26
+ class IdentityExpressionFilter < ExpressionFilter
27
+ def initialize
28
+ end
29
+
30
+ def default?
31
+ false
32
+ end
33
+
34
+ def apply_filter(expr, _value)
35
+ return expr
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+ require 'capybara/selector/filters/base'
3
+
4
+ module Capybara
5
+ class Selector
6
+ module Filters
7
+ class NodeFilter < Base
8
+ def matches?(node, value)
9
+ return true if skip?(value)
10
+
11
+ if !valid_value?(value)
12
+ msg = "Invalid value #{value.inspect} passed to filter #{@name} - "
13
+ if default?
14
+ warn msg + "defaulting to #{default}"
15
+ value = default
16
+ else
17
+ warn msg + "skipping"
18
+ return true
19
+ end
20
+ end
21
+
22
+ @block.call(node, value)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,276 @@
1
+ # frozen_string_literal: true
2
+ require 'capybara/selector/filter_set'
3
+ require 'capybara/selector/css'
4
+ require 'xpath'
5
+
6
+ #Patch XPath to allow a nil condition in where
7
+ module XPath
8
+ class Renderer
9
+ undef :where if method_defined?(:where)
10
+ def where(on, condition)
11
+ condition = condition.to_s
12
+ if !condition.empty?
13
+ "#{on}[#{condition}]"
14
+ else
15
+ "#{on}"
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ module Capybara
22
+ class Selector
23
+
24
+ attr_reader :name, :format
25
+
26
+ class << self
27
+ def all
28
+ @selectors ||= {}
29
+ end
30
+
31
+ def add(name, &block)
32
+ all[name.to_sym] = Capybara::Selector.new(name.to_sym, &block)
33
+ end
34
+
35
+ def update(name, &block)
36
+ all[name.to_sym].instance_eval(&block)
37
+ end
38
+
39
+ def remove(name)
40
+ all.delete(name.to_sym)
41
+ end
42
+ end
43
+
44
+ def initialize(name, &block)
45
+ @name = name
46
+ @filter_set = FilterSet.add(name){}
47
+ @match = nil
48
+ @label = nil
49
+ @failure_message = nil
50
+ @description = nil
51
+ @format = nil
52
+ @expression = nil
53
+ @expression_filters = {}
54
+ @default_visibility = nil
55
+ instance_eval(&block)
56
+ end
57
+
58
+ def custom_filters
59
+ @filter_set.filters
60
+ end
61
+
62
+ def node_filters
63
+ @filter_set.node_filters
64
+ end
65
+
66
+ def expression_filters
67
+ @filter_set.expression_filters
68
+ end
69
+
70
+ ##
71
+ #
72
+ # Define a selector by an xpath expression
73
+ #
74
+ # @overload xpath(*expression_filters, &block)
75
+ # @param [Array<Symbol>] expression_filters ([]) Names of filters that can be implemented via this expression
76
+ # @yield [locator, options] The block to use to generate the XPath expression
77
+ # @yieldparam [String] locator The locator string passed to the query
78
+ # @yieldparam [Hash] options The options hash passed to the query
79
+ # @yieldreturn [#to_xpath, #to_s] An object that can produce an xpath expression
80
+ #
81
+ # @overload xpath()
82
+ # @return [#call] The block that will be called to generate the XPath expression
83
+ #
84
+ def xpath(*expression_filters, &block)
85
+ if block
86
+ @format, @expression = :xpath, block
87
+ expression_filters.flatten.each { |ef| custom_filters[ef] = Filters::IdentityExpressionFilter.new }
88
+ end
89
+ format == :xpath ? @expression : nil
90
+ end
91
+
92
+ ##
93
+ #
94
+ # Define a selector by a CSS selector
95
+ #
96
+ # @overload css(*expression_filters, &block)
97
+ # @param [Array<Symbol>] expression_filters ([]) Names of filters that can be implemented via this CSS selector
98
+ # @yield [locator, options] The block to use to generate the CSS selector
99
+ # @yieldparam [String] locator The locator string passed to the query
100
+ # @yieldparam [Hash] options The options hash passed to the query
101
+ # @yieldreturn [#to_s] An object that can produce a CSS selector
102
+ #
103
+ # @overload css()
104
+ # @return [#call] The block that will be called to generate the CSS selector
105
+ #
106
+ def css(*expression_filters, &block)
107
+ if block
108
+ @format, @expression = :css, block
109
+ expression_filters.flatten.each { |ef| custom_filters[ef] = nil }
110
+ end
111
+ format == :css ? @expression : nil
112
+ end
113
+
114
+ ##
115
+ #
116
+ # Automatic selector detection
117
+ #
118
+ # @yield [locator] This block takes the passed in locator string and returns whether or not it matches the selector
119
+ # @yieldparam [String], locator The locator string used to determin if it matches the selector
120
+ # @yieldreturn [Boolean] Whether this selector matches the locator string
121
+ # @return [#call] The block that will be used to detect selector match
122
+ #
123
+ def match(&block)
124
+ @match = block if block
125
+ @match
126
+ end
127
+
128
+ ##
129
+ #
130
+ # Set/get a descriptive label for the selector
131
+ #
132
+ # @overload label(label)
133
+ # @param [String] label A descriptive label for this selector - used in error messages
134
+ # @overload label()
135
+ # @return [String] The currently set label
136
+ #
137
+ def label(label=nil)
138
+ @label = label if label
139
+ @label
140
+ end
141
+
142
+ ##
143
+ #
144
+ # Description of the selector
145
+ #
146
+ # @param [Hash] options The options of the query used to generate the description
147
+ # @return [String] Description of the selector when used with the options passed
148
+ #
149
+ def description(options={})
150
+ @filter_set.description(options)
151
+ end
152
+
153
+ def call(locator, options={})
154
+ if format
155
+ # @expression.call(locator, options.select {|k,v| @expression_filters.include?(k)})
156
+ @expression.call(locator, options)
157
+ else
158
+ warn "Selector has no format"
159
+ end
160
+ end
161
+
162
+ ##
163
+ #
164
+ # Should this selector be used for the passed in locator
165
+ #
166
+ # This is used by the automatic selector selection mechanism when no selector type is passed to a selector query
167
+ #
168
+ # @param [String] locator The locator passed to the query
169
+ # @return [Boolean] Whether or not to use this selector
170
+ #
171
+ def match?(locator)
172
+ @match and @match.call(locator)
173
+ end
174
+
175
+ ##
176
+ #
177
+ # Define a non-expression filter for use with this selector
178
+ #
179
+ # @overload filter(name, *types, options={}, &block)
180
+ # @param [Symbol] name The filter name
181
+ # @param [Array<Symbol>] types The types of the filter - currently valid types are [:boolean]
182
+ # @param [Hash] options ({}) Options of the filter
183
+ # @option options [Array<>] :valid_values Valid values for this filter
184
+ # @option options :default The default value of the filter (if any)
185
+ # @option options :skip_if Value of the filter that will cause it to be skipped
186
+ #
187
+ def filter(name, *types_and_options, &block)
188
+ options = types_and_options.last.is_a?(Hash) ? types_and_options.pop.dup : {}
189
+ types_and_options.each { |k| options[k] = true }
190
+ custom_filters[name] = Filters::NodeFilter.new(name, block, options)
191
+ end
192
+
193
+ def expression_filter(name, *types_and_options, &block)
194
+ options = types_and_options.last.is_a?(Hash) ? types_and_options.pop.dup : {}
195
+ types_and_options.each { |k| options[k] = true }
196
+ custom_filters[name] = Filters::ExpressionFilter.new(name, block, options)
197
+ end
198
+
199
+ def filter_set(name, filters_to_use = nil)
200
+ f_set = FilterSet.all[name]
201
+ f_set.filters.each do |n, filter|
202
+ custom_filters[n] = filter if filters_to_use.nil? || filters_to_use.include?(n)
203
+ end
204
+
205
+ f_set.descriptions.each { |desc| @filter_set.describe(&desc) }
206
+ end
207
+
208
+ def describe &block
209
+ @filter_set.describe(&block)
210
+ end
211
+
212
+ ##
213
+ #
214
+ # Set the default visibility mode that shouble be used if no visibile option is passed when using the selector.
215
+ # If not specified will default to the behavior indicated by Capybara.ignore_hidden_elements
216
+ #
217
+ # @param [Symbol] default_visibility Only find elements with the specified visibility:
218
+ # * :all - finds visible and invisible elements.
219
+ # * :hidden - only finds invisible elements.
220
+ # * :visible - only finds visible elements.
221
+ def visible(default_visibility)
222
+ @default_visibility = default_visibility
223
+ end
224
+
225
+ def default_visibility(fallback = Capybara.ignore_hidden_elements)
226
+ if @default_visibility.nil?
227
+ fallback
228
+ else
229
+ @default_visibility
230
+ end
231
+ end
232
+
233
+ private
234
+
235
+ def locate_field(xpath, locator, options={})
236
+ locate_xpath = xpath #need to save original xpath for the label wrap
237
+ if locator
238
+ locator = locator.to_s
239
+ attr_matchers = XPath.attr(:id).equals(locator).or(
240
+ XPath.attr(:name).equals(locator)).or(
241
+ XPath.attr(:placeholder).equals(locator)).or(
242
+ XPath.attr(:id).equals(XPath.anywhere(:label)[XPath.string.n.is(locator)].attr(:for)))
243
+ attr_matchers = attr_matchers.or XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
244
+
245
+ locate_xpath = locate_xpath[attr_matchers]
246
+ locate_xpath = locate_xpath.union(XPath.descendant(:label)[XPath.string.n.is(locator)].descendant(xpath))
247
+ end
248
+
249
+ # locate_xpath = [:name, :placeholder].inject(locate_xpath) { |memo, ef| memo[find_by_attr(ef, options[ef])] }
250
+ locate_xpath
251
+ end
252
+
253
+ def describe_all_expression_filters(opts={})
254
+ expression_filters.map { |ef| " with #{ef} #{opts[ef]}" if opts.has_key?(ef) }.join
255
+ end
256
+
257
+ def find_by_attr(attribute, value)
258
+ finder_name = "find_by_#{attribute}_attr"
259
+ if respond_to?(finder_name, true)
260
+ send(finder_name, value)
261
+ else
262
+ value ? XPath.attr(attribute).equals(value) : nil
263
+ end
264
+ end
265
+
266
+ def find_by_class_attr(classes)
267
+ if classes
268
+ Array(classes).map do |klass|
269
+ "contains(concat(' ',normalize-space(@class),' '),' #{klass} ')"
270
+ end.join(" and ").to_sym
271
+ else
272
+ nil
273
+ end
274
+ end
275
+ end
276
+ end