capybara 2.18.0 → 3.0.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.
- checksums.yaml +4 -4
- data/History.md +55 -1
- data/README.md +18 -17
- data/lib/capybara/config.rb +11 -58
- data/lib/capybara/cucumber.rb +2 -3
- data/lib/capybara/driver/base.rb +15 -16
- data/lib/capybara/driver/node.rb +5 -4
- data/lib/capybara/dsl.rb +1 -0
- data/lib/capybara/helpers.rb +19 -29
- data/lib/capybara/minitest/spec.rb +15 -14
- data/lib/capybara/minitest.rb +139 -138
- data/lib/capybara/node/actions.rb +60 -81
- data/lib/capybara/node/base.rb +11 -18
- data/lib/capybara/node/document.rb +2 -2
- data/lib/capybara/node/document_matchers.rb +8 -8
- data/lib/capybara/node/element.rb +30 -40
- data/lib/capybara/node/finders.rb +62 -70
- data/lib/capybara/node/matchers.rb +50 -71
- data/lib/capybara/node/simple.rb +11 -17
- data/lib/capybara/queries/ancestor_query.rb +11 -7
- data/lib/capybara/queries/base_query.rb +22 -18
- data/lib/capybara/queries/current_path_query.rb +8 -24
- data/lib/capybara/queries/match_query.rb +3 -7
- data/lib/capybara/queries/selector_query.rb +92 -95
- data/lib/capybara/queries/sibling_query.rb +4 -4
- data/lib/capybara/queries/text_query.rb +35 -35
- data/lib/capybara/queries/title_query.rb +8 -11
- data/lib/capybara/rack_test/browser.rb +15 -18
- data/lib/capybara/rack_test/css_handlers.rb +6 -4
- data/lib/capybara/rack_test/driver.rb +6 -10
- data/lib/capybara/rack_test/form.rb +50 -40
- data/lib/capybara/rack_test/node.rb +93 -63
- data/lib/capybara/rails.rb +2 -6
- data/lib/capybara/result.rb +22 -22
- data/lib/capybara/rspec/compound.rb +5 -10
- data/lib/capybara/rspec/features.rb +17 -48
- data/lib/capybara/rspec/matcher_proxies.rb +31 -15
- data/lib/capybara/rspec/matchers.rb +70 -61
- data/lib/capybara/rspec.rb +5 -10
- data/lib/capybara/selector/css.rb +6 -11
- data/lib/capybara/selector/filter.rb +1 -17
- data/lib/capybara/selector/filter_set.rb +18 -15
- data/lib/capybara/selector/filters/base.rb +7 -6
- data/lib/capybara/selector/filters/expression_filter.rb +6 -23
- data/lib/capybara/selector/filters/node_filter.rb +2 -12
- data/lib/capybara/selector/selector.rb +28 -34
- data/lib/capybara/selector.rb +129 -117
- data/lib/capybara/selenium/driver.rb +131 -125
- data/lib/capybara/selenium/node.rb +197 -115
- data/lib/capybara/server.rb +3 -2
- data/lib/capybara/session/config.rb +47 -67
- data/lib/capybara/session/matchers.rb +8 -7
- data/lib/capybara/session.rb +138 -224
- data/lib/capybara/spec/public/test.js +25 -4
- data/lib/capybara/spec/session/accept_alert_spec.rb +1 -0
- data/lib/capybara/spec/session/accept_confirm_spec.rb +3 -2
- data/lib/capybara/spec/session/accept_prompt_spec.rb +1 -0
- data/lib/capybara/spec/session/all_spec.rb +31 -18
- data/lib/capybara/spec/session/ancestor_spec.rb +6 -8
- data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +6 -5
- data/lib/capybara/spec/session/assert_current_path.rb +12 -11
- data/lib/capybara/spec/session/assert_selector.rb +1 -0
- data/lib/capybara/spec/session/assert_text.rb +23 -23
- data/lib/capybara/spec/session/assert_title.rb +13 -3
- data/lib/capybara/spec/session/attach_file_spec.rb +51 -30
- data/lib/capybara/spec/session/body_spec.rb +1 -0
- data/lib/capybara/spec/session/check_spec.rb +7 -6
- data/lib/capybara/spec/session/choose_spec.rb +5 -4
- data/lib/capybara/spec/session/click_button_spec.rb +24 -32
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +8 -7
- data/lib/capybara/spec/session/click_link_spec.rb +8 -7
- data/lib/capybara/spec/session/current_scope_spec.rb +4 -3
- data/lib/capybara/spec/session/current_url_spec.rb +17 -6
- data/lib/capybara/spec/session/dismiss_confirm_spec.rb +1 -1
- data/lib/capybara/spec/session/dismiss_prompt_spec.rb +1 -0
- data/lib/capybara/spec/session/element/assert_match_selector.rb +1 -1
- data/lib/capybara/spec/session/element/match_xpath_spec.rb +1 -1
- data/lib/capybara/spec/session/element/matches_selector_spec.rb +5 -5
- data/lib/capybara/spec/session/evaluate_async_script_spec.rb +3 -2
- data/lib/capybara/spec/session/evaluate_script_spec.rb +4 -3
- data/lib/capybara/spec/session/execute_script_spec.rb +4 -3
- data/lib/capybara/spec/session/fill_in_spec.rb +30 -5
- data/lib/capybara/spec/session/find_button_spec.rb +4 -3
- data/lib/capybara/spec/session/find_by_id_spec.rb +2 -1
- data/lib/capybara/spec/session/find_field_spec.rb +9 -15
- data/lib/capybara/spec/session/find_link_spec.rb +6 -5
- data/lib/capybara/spec/session/find_spec.rb +37 -31
- data/lib/capybara/spec/session/first_spec.rb +60 -33
- data/lib/capybara/spec/session/frame/frame_title_spec.rb +23 -0
- data/lib/capybara/spec/session/frame/frame_url_spec.rb +23 -0
- data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +2 -1
- data/lib/capybara/spec/session/frame/within_frame_spec.rb +9 -16
- data/lib/capybara/spec/session/go_back_spec.rb +1 -0
- data/lib/capybara/spec/session/go_forward_spec.rb +1 -0
- data/lib/capybara/spec/session/has_all_selectors_spec.rb +15 -15
- data/lib/capybara/spec/session/has_button_spec.rb +2 -1
- data/lib/capybara/spec/session/has_css_spec.rb +3 -2
- data/lib/capybara/spec/session/has_current_path_spec.rb +12 -28
- data/lib/capybara/spec/session/has_field_spec.rb +4 -3
- data/lib/capybara/spec/session/has_link_spec.rb +1 -0
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +17 -17
- data/lib/capybara/spec/session/has_select_spec.rb +30 -29
- data/lib/capybara/spec/session/has_selector_spec.rb +5 -4
- data/lib/capybara/spec/session/has_table_spec.rb +2 -1
- data/lib/capybara/spec/session/has_text_spec.rb +9 -13
- data/lib/capybara/spec/session/has_title_spec.rb +1 -0
- data/lib/capybara/spec/session/has_xpath_spec.rb +1 -0
- data/lib/capybara/spec/session/headers.rb +2 -1
- data/lib/capybara/spec/session/html_spec.rb +1 -0
- data/lib/capybara/spec/session/node_spec.rb +91 -56
- data/lib/capybara/spec/session/node_wrapper_spec.rb +36 -0
- data/lib/capybara/spec/session/refresh_spec.rb +6 -2
- data/lib/capybara/spec/session/reset_session_spec.rb +19 -0
- data/lib/capybara/spec/session/response_code.rb +1 -0
- data/lib/capybara/spec/session/save_and_open_page_spec.rb +1 -0
- data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +6 -11
- data/lib/capybara/spec/session/save_page_spec.rb +1 -17
- data/lib/capybara/spec/session/save_screenshot_spec.rb +3 -3
- data/lib/capybara/spec/session/select_spec.rb +20 -20
- data/lib/capybara/spec/session/selectors_spec.rb +2 -2
- data/lib/capybara/spec/session/sibling_spec.rb +1 -1
- data/lib/capybara/spec/session/text_spec.rb +17 -3
- data/lib/capybara/spec/session/title_spec.rb +11 -1
- data/lib/capybara/spec/session/uncheck_spec.rb +4 -3
- data/lib/capybara/spec/session/unselect_spec.rb +6 -5
- data/lib/capybara/spec/session/visit_spec.rb +9 -3
- data/lib/capybara/spec/session/window/become_closed_spec.rb +2 -1
- data/lib/capybara/spec/session/window/current_window_spec.rb +1 -0
- data/lib/capybara/spec/session/window/open_new_window_spec.rb +1 -0
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +2 -1
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +2 -1
- data/lib/capybara/spec/session/window/window_spec.rb +12 -12
- data/lib/capybara/spec/session/window/windows_spec.rb +2 -3
- data/lib/capybara/spec/session/window/within_window_spec.rb +15 -71
- data/lib/capybara/spec/session/within_spec.rb +1 -0
- data/lib/capybara/spec/spec_helper.rb +34 -18
- data/lib/capybara/spec/test_app.rb +17 -9
- data/lib/capybara/spec/views/form.erb +7 -0
- data/lib/capybara/spec/views/with_html.erb +23 -1
- data/lib/capybara/spec/views/within_frames.erb +4 -1
- data/lib/capybara/version.rb +2 -1
- data/lib/capybara/window.rb +6 -10
- data/lib/capybara.rb +28 -25
- data/spec/basic_node_spec.rb +1 -0
- data/spec/capybara_spec.rb +11 -50
- data/spec/dsl_spec.rb +5 -13
- data/spec/filter_set_spec.rb +5 -4
- data/spec/fixtures/selenium_driver_rspec_failure.rb +2 -1
- data/spec/fixtures/selenium_driver_rspec_success.rb +3 -2
- data/spec/minitest_spec.rb +4 -3
- data/spec/minitest_spec_spec.rb +3 -2
- data/spec/per_session_config_spec.rb +9 -8
- data/spec/rack_test_spec.rb +21 -20
- data/spec/result_spec.rb +17 -16
- data/spec/rspec/features_spec.rb +17 -14
- data/spec/rspec/scenarios_spec.rb +5 -7
- data/spec/rspec/shared_spec_matchers.rb +96 -99
- data/spec/rspec/views_spec.rb +2 -1
- data/spec/rspec_matchers_spec.rb +18 -2
- data/spec/rspec_spec.rb +11 -15
- data/spec/selector_spec.rb +5 -6
- data/spec/selenium_spec_chrome.rb +9 -4
- data/spec/selenium_spec_edge.rb +27 -0
- data/spec/selenium_spec_ie.rb +31 -0
- data/spec/selenium_spec_marionette.rb +28 -12
- data/spec/server_spec.rb +33 -33
- data/spec/session_spec.rb +2 -1
- data/spec/shared_selenium_session.rb +36 -22
- data/spec/spec_helper.rb +3 -6
- metadata +68 -85
- data/lib/capybara/query.rb +0 -7
- data/spec/selenium_spec_firefox.rb +0 -68
@@ -1,36 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Capybara
|
3
4
|
module Queries
|
4
5
|
class SelectorQuery < Queries::BaseQuery
|
5
6
|
attr_accessor :selector, :locator, :options, :expression, :find, :negative
|
6
7
|
|
7
|
-
VALID_KEYS = COUNT_KEYS + [
|
8
|
-
VALID_MATCH = [
|
8
|
+
VALID_KEYS = COUNT_KEYS + %i[text id class visible exact exact_text match wait filter_set]
|
9
|
+
VALID_MATCH = %i[first smart prefer_exact one].freeze
|
9
10
|
|
10
|
-
def initialize(*args, &filter_block)
|
11
|
+
def initialize(*args, session_options:, **options, &filter_block)
|
11
12
|
@resolved_node = nil
|
12
|
-
@options =
|
13
|
+
@options = options.dup
|
13
14
|
super(@options)
|
15
|
+
self.session_options = session_options
|
14
16
|
|
17
|
+
@selector = find_selector(args[0].is_a?(Symbol) ? args.shift : args[0])
|
18
|
+
@locator = args.shift
|
15
19
|
@filter_block = filter_block
|
16
20
|
|
17
|
-
|
18
|
-
@selector = Selector.all.fetch(args.shift) do |selector_type|
|
19
|
-
raise ArgumentError, "Unknown selector type (:#{selector_type})"
|
20
|
-
end
|
21
|
-
@locator = args.shift
|
22
|
-
else
|
23
|
-
@selector = Selector.all.values.find { |s| s.match?(args[0]) }
|
24
|
-
@locator = args.shift
|
25
|
-
end
|
26
|
-
@selector ||= Selector.all[session_options.default_selector]
|
27
|
-
|
28
|
-
warn "Unused parameters passed to #{self.class.name} : #{args}" unless args.empty?
|
29
|
-
|
30
|
-
# for compatibility with Capybara 2.0
|
31
|
-
if session_options.exact_options and @selector == Selector.all[:option]
|
32
|
-
@options[:exact] = true
|
33
|
-
end
|
21
|
+
raise ArgumentError, "Unused parameters passed to #{self.class.name} : #{args}" unless args.empty?
|
34
22
|
|
35
23
|
@expression = @selector.call(@locator, @options.merge(enable_aria_label: session_options.enable_aria_label))
|
36
24
|
|
@@ -40,10 +28,10 @@ module Capybara
|
|
40
28
|
end
|
41
29
|
|
42
30
|
def name; selector.name; end
|
43
|
-
def label; selector.label
|
31
|
+
def label; selector.label || selector.name; end
|
44
32
|
|
45
33
|
def description
|
46
|
-
@description =
|
34
|
+
@description = "".dup
|
47
35
|
@description << "visible " if visible == :visible
|
48
36
|
@description << "non-visible " if visible == :hidden
|
49
37
|
@description << "#{label} #{locator.inspect}"
|
@@ -58,80 +46,39 @@ module Capybara
|
|
58
46
|
end
|
59
47
|
|
60
48
|
def matches_filters?(node)
|
61
|
-
if options[:text]
|
62
|
-
|
63
|
-
options[:text]
|
64
|
-
else
|
65
|
-
if exact_text == true
|
66
|
-
/\A#{Regexp.escape(options[:text].to_s)}\z/
|
67
|
-
else
|
68
|
-
Regexp.escape(options[:text].to_s)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
text_visible = visible
|
72
|
-
text_visible = :all if text_visible == :hidden
|
73
|
-
return false if not node.text(text_visible).match(regexp)
|
74
|
-
end
|
75
|
-
|
76
|
-
if exact_text.is_a?(String)
|
77
|
-
regexp = /\A#{Regexp.escape(options[:exact_text])}\z/
|
78
|
-
text_visible = visible
|
79
|
-
text_visible = :all if text_visible == :hidden
|
80
|
-
return false if not node.text(text_visible).match(regexp)
|
81
|
-
end
|
49
|
+
return false if options[:text] && !matches_text_filter(node, options[:text])
|
50
|
+
return false if exact_text.is_a?(String) && !matches_exact_text_filter(node, exact_text)
|
82
51
|
|
83
52
|
case visible
|
84
|
-
|
85
|
-
|
53
|
+
when :visible then return false unless node.visible?
|
54
|
+
when :hidden then return false if node.visible?
|
86
55
|
end
|
87
56
|
|
88
|
-
|
89
|
-
if options.has_key?(name)
|
90
|
-
filter.matches?(node, options[name])
|
91
|
-
elsif filter.default?
|
92
|
-
filter.matches?(node, filter.default)
|
93
|
-
else
|
94
|
-
true
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
res &&= if node.respond_to?(:session)
|
99
|
-
node.session.using_wait_time(0){ @filter_block.call(node) }
|
100
|
-
else
|
101
|
-
@filter_block.call(node)
|
102
|
-
end unless @filter_block.nil?
|
103
|
-
|
104
|
-
res
|
105
|
-
|
57
|
+
matches_node_filters?(node) && matches_filter_block?(node)
|
106
58
|
rescue *(node.respond_to?(:session) ? node.session.driver.invalid_element_errors : [])
|
107
59
|
return false
|
108
60
|
end
|
109
61
|
|
110
62
|
def visible
|
111
|
-
case (vis = options.fetch(:visible){ @selector.default_visibility(session_options.ignore_hidden_elements) })
|
112
|
-
|
113
|
-
|
114
|
-
|
63
|
+
case (vis = options.fetch(:visible) { @selector.default_visibility(session_options.ignore_hidden_elements) })
|
64
|
+
when true then :visible
|
65
|
+
when false then :all
|
66
|
+
else vis
|
115
67
|
end
|
116
68
|
end
|
117
69
|
|
118
70
|
def exact?
|
119
|
-
|
120
|
-
options.fetch(:exact, session_options.exact)
|
71
|
+
supports_exact? ? options.fetch(:exact, session_options.exact) : false
|
121
72
|
end
|
122
73
|
|
123
74
|
def match
|
124
75
|
options.fetch(:match, session_options.match)
|
125
76
|
end
|
126
77
|
|
127
|
-
def xpath(exact=nil)
|
128
|
-
exact =
|
78
|
+
def xpath(exact = nil)
|
79
|
+
exact = exact? if exact.nil?
|
129
80
|
expr = apply_expression_filters(@expression)
|
130
|
-
expr = if expr.respond_to?(:to_xpath)
|
131
|
-
expr.to_xpath(:exact)
|
132
|
-
else
|
133
|
-
expr.to_s
|
134
|
-
end
|
81
|
+
expr = exact ? expr.to_xpath(:exact) : expr.to_s if expr.respond_to?(:to_xpath)
|
135
82
|
filtered_xpath(expr)
|
136
83
|
end
|
137
84
|
|
@@ -144,9 +91,9 @@ module Capybara
|
|
144
91
|
@resolved_node = node
|
145
92
|
node.synchronize do
|
146
93
|
children = if selector.format == :css
|
147
|
-
node.find_css(
|
94
|
+
node.find_css(css)
|
148
95
|
else
|
149
|
-
node.find_xpath(
|
96
|
+
node.find_xpath(xpath(exact))
|
150
97
|
end.map do |child|
|
151
98
|
if node.is_a?(Capybara::Node::Base)
|
152
99
|
Capybara::Node::Element.new(node.session, child, node, self)
|
@@ -163,14 +110,45 @@ module Capybara
|
|
163
110
|
@expression.respond_to? :to_xpath
|
164
111
|
end
|
165
112
|
|
166
|
-
|
113
|
+
private
|
114
|
+
|
115
|
+
def find_selector(locator)
|
116
|
+
selector = if locator.is_a?(Symbol)
|
117
|
+
Selector.all.fetch(locator) { |sel_type| raise ArgumentError, "Unknown selector type (:#{sel_type})" }
|
118
|
+
else
|
119
|
+
Selector.all.values.find { |s| s.match?(locator) }
|
120
|
+
end
|
121
|
+
selector || Selector.all[session_options.default_selector]
|
122
|
+
end
|
167
123
|
|
168
124
|
def valid_keys
|
169
125
|
VALID_KEYS + custom_keys
|
170
126
|
end
|
171
127
|
|
128
|
+
def matches_node_filters?(node)
|
129
|
+
node_filters.all? do |name, filter|
|
130
|
+
if options.key?(name)
|
131
|
+
filter.matches?(node, options[name])
|
132
|
+
elsif filter.default?
|
133
|
+
filter.matches?(node, filter.default)
|
134
|
+
else
|
135
|
+
true
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def matches_filter_block?(node)
|
141
|
+
return true unless @filter_block
|
142
|
+
|
143
|
+
if node.respond_to?(:session)
|
144
|
+
node.session.using_wait_time(0) { @filter_block.call(node) }
|
145
|
+
else
|
146
|
+
@filter_block.call(node)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
172
150
|
def node_filters
|
173
|
-
if options.
|
151
|
+
if options.key?(:filter_set)
|
174
152
|
::Capybara::Selector::FilterSet.all[options[:filter_set]].node_filters
|
175
153
|
else
|
176
154
|
@selector.node_filters
|
@@ -179,7 +157,7 @@ module Capybara
|
|
179
157
|
|
180
158
|
def expression_filters
|
181
159
|
filters = @selector.expression_filters
|
182
|
-
filters.merge ::Capybara::Selector::FilterSet.all[options[:filter_set]].expression_filters if options.
|
160
|
+
filters.merge ::Capybara::Selector::FilterSet.all[options[:filter_set]].expression_filters if options.key?(:filter_set)
|
183
161
|
filters
|
184
162
|
end
|
185
163
|
|
@@ -195,13 +173,13 @@ module Capybara
|
|
195
173
|
end
|
196
174
|
|
197
175
|
def filtered_xpath(expr)
|
198
|
-
if options.
|
176
|
+
if options.key?(:id) || options.key?(:class)
|
199
177
|
expr = "(#{expr})"
|
200
|
-
expr = "#{expr}[#{XPath.attr(:id) == options[:id]}]" if options.
|
201
|
-
if options.
|
178
|
+
expr = "#{expr}[#{XPath.attr(:id) == options[:id]}]" if options.key?(:id) && !custom_keys.include?(:id)
|
179
|
+
if options.key?(:class) && !custom_keys.include?(:class)
|
202
180
|
class_xpath = Array(options[:class]).map do |klass|
|
203
|
-
|
204
|
-
end.
|
181
|
+
XPath.attr(:class).contains_word(klass)
|
182
|
+
end.reduce(:&)
|
205
183
|
expr = "#{expr}[#{class_xpath}]"
|
206
184
|
end
|
207
185
|
end
|
@@ -209,12 +187,12 @@ module Capybara
|
|
209
187
|
end
|
210
188
|
|
211
189
|
def filtered_css(expr)
|
212
|
-
if options.
|
190
|
+
if options.key?(:id) || options.key?(:class)
|
213
191
|
css_selectors = expr.split(',').map(&:rstrip)
|
214
192
|
expr = css_selectors.map do |sel|
|
215
|
-
|
216
|
-
|
217
|
-
|
193
|
+
sel += "##{Capybara::Selector::CSS.escape(options[:id])}" if options.key?(:id) && !custom_keys.include?(:id)
|
194
|
+
sel += Array(options[:class]).map { |k| ".#{Capybara::Selector::CSS.escape(k)}" }.join if options.key?(:class) && !custom_keys.include?(:class)
|
195
|
+
sel
|
218
196
|
end.join(", ")
|
219
197
|
end
|
220
198
|
expr
|
@@ -222,7 +200,7 @@ module Capybara
|
|
222
200
|
|
223
201
|
def apply_expression_filters(expr)
|
224
202
|
expression_filters.inject(expr) do |memo, (name, ef)|
|
225
|
-
if options.
|
203
|
+
if options.key?(name)
|
226
204
|
ef.apply_filter(memo, options[name])
|
227
205
|
elsif ef.default?
|
228
206
|
ef.apply_filter(memo, ef.default)
|
@@ -233,9 +211,8 @@ module Capybara
|
|
233
211
|
end
|
234
212
|
|
235
213
|
def warn_exact_usage
|
236
|
-
|
237
|
-
|
238
|
-
end
|
214
|
+
return unless options.key?(:exact) && !supports_exact?
|
215
|
+
warn "The :exact option only has an effect on queries using the XPath#is method. Using it with the query \"#{expression}\" has no effect."
|
239
216
|
end
|
240
217
|
|
241
218
|
def exact_text
|
@@ -246,6 +223,26 @@ module Capybara
|
|
246
223
|
@resolved_node && !(@resolved_node.is_a?(::Capybara::Node::Document) ||
|
247
224
|
(@resolved_node.is_a?(::Capybara::Node::Simple) && @resolved_node.path == '/'))
|
248
225
|
end
|
226
|
+
|
227
|
+
def matches_text_filter(node, text_option)
|
228
|
+
regexp = if text_option.is_a?(Regexp)
|
229
|
+
text_option
|
230
|
+
elsif exact_text == true
|
231
|
+
/\A#{Regexp.escape(text_option.to_s)}\z/
|
232
|
+
else
|
233
|
+
Regexp.escape(text_option.to_s)
|
234
|
+
end
|
235
|
+
text_visible = visible
|
236
|
+
text_visible = :all if text_visible == :hidden
|
237
|
+
node.text(text_visible).match(regexp)
|
238
|
+
end
|
239
|
+
|
240
|
+
def matches_exact_text_filter(node, exact_text_option)
|
241
|
+
regexp = /\A#{Regexp.escape(exact_text_option)}\z/
|
242
|
+
text_visible = visible
|
243
|
+
text_visible = :all if text_visible == :hidden
|
244
|
+
node.text(text_visible).match(regexp)
|
245
|
+
end
|
249
246
|
end
|
250
247
|
end
|
251
248
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Capybara
|
3
4
|
module Queries
|
4
5
|
class SiblingQuery < MatchQuery
|
@@ -7,7 +8,7 @@ module Capybara
|
|
7
8
|
@sibling_node = node
|
8
9
|
node.synchronize do
|
9
10
|
match_results = super(node.session.current_scope, exact)
|
10
|
-
node.all(:xpath, XPath.preceding_sibling
|
11
|
+
node.all(:xpath, XPath.preceding_sibling + XPath.following_sibling) do |el|
|
11
12
|
match_results.include?(el)
|
12
13
|
end
|
13
14
|
end
|
@@ -15,9 +16,8 @@ module Capybara
|
|
15
16
|
|
16
17
|
def description
|
17
18
|
desc = super
|
18
|
-
|
19
|
-
|
20
|
-
end
|
19
|
+
sibling_query = @sibling_node && @sibling_node.instance_variable_get(:@query)
|
20
|
+
desc += " that is a sibling of #{sibling_query.description}" if sibling_query
|
21
21
|
desc
|
22
22
|
end
|
23
23
|
end
|
@@ -1,22 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Capybara
|
3
4
|
# @api private
|
4
5
|
module Queries
|
5
6
|
class TextQuery < BaseQuery
|
6
|
-
def initialize(
|
7
|
-
@type =
|
8
|
-
|
9
|
-
|
7
|
+
def initialize(type = nil, expected_text, session_options:, **options) # rubocop:disable Style/OptionalArguments
|
8
|
+
@type = if type.nil?
|
9
|
+
Capybara.ignore_hidden_elements || Capybara.visible_text_only ? :visible : :all
|
10
|
+
else
|
11
|
+
type
|
12
|
+
end
|
13
|
+
|
14
|
+
@expected_text = expected_text.is_a?(Regexp) ? expected_text : expected_text.to_s
|
15
|
+
@options = options
|
10
16
|
super(@options)
|
17
|
+
self.session_options = session_options
|
11
18
|
|
12
|
-
@
|
19
|
+
@search_regexp = Capybara::Helpers.to_regexp(@expected_text, exact: exact?)
|
13
20
|
|
14
|
-
@expected_text = args.shift
|
15
|
-
unless @expected_text.is_a?(Regexp)
|
16
|
-
@expected_text = Capybara::Helpers.normalize_whitespace(@expected_text)
|
17
|
-
end
|
18
|
-
@search_regexp = Capybara::Helpers.to_regexp(@expected_text, nil, exact?)
|
19
|
-
warn "Unused parameters passed to #{self.class.name} : #{args}" unless args.empty?
|
20
21
|
assert_valid_keys
|
21
22
|
end
|
22
23
|
|
@@ -42,48 +43,47 @@ module Capybara
|
|
42
43
|
end
|
43
44
|
end
|
44
45
|
|
45
|
-
|
46
|
+
private
|
46
47
|
|
47
48
|
def exact?
|
48
49
|
options.fetch(:exact, session_options.exact_text)
|
49
50
|
end
|
50
51
|
|
51
52
|
def build_message(report_on_invisible)
|
52
|
-
message =
|
53
|
+
message = "".dup
|
53
54
|
unless (COUNT_KEYS & @options.keys).empty?
|
54
55
|
message << " but found #{@count} #{Capybara::Helpers.declension('time', 'times', @count)}"
|
55
56
|
end
|
56
57
|
message << " in #{@actual_text.inspect}"
|
57
58
|
|
58
59
|
details_message = []
|
60
|
+
details_message << case_insensitive_message if @node and !@expected_text.is_a? Regexp
|
61
|
+
details_message << invisible_message if @node and check_visible_text? and report_on_invisible
|
62
|
+
details_message.compact!
|
59
63
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
if insensitive_count != @count
|
64
|
-
details_message << "it was found #{insensitive_count} #{Capybara::Helpers.declension("time", "times", insensitive_count)} using a case insensitive search"
|
65
|
-
end
|
66
|
-
end
|
64
|
+
message << ". (However, #{details_message.join(' and ')}.)" unless details_message.empty?
|
65
|
+
message
|
66
|
+
end
|
67
67
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
details_message << "it was found #{invisible_count} #{Capybara::Helpers.declension("time", "times", invisible_count)} including non-visible text"
|
74
|
-
end
|
75
|
-
rescue
|
76
|
-
# An error getting the non-visible text (if element goes out of scope) should not affect the response
|
77
|
-
end
|
68
|
+
def case_insensitive_message
|
69
|
+
insensitive_regexp = Capybara::Helpers.to_regexp(@expected_text, options: Regexp::IGNORECASE)
|
70
|
+
insensitive_count = @actual_text.scan(insensitive_regexp).size
|
71
|
+
if insensitive_count != @count
|
72
|
+
"it was found #{insensitive_count} #{Capybara::Helpers.declension("time", "times", insensitive_count)} using a case insensitive search"
|
78
73
|
end
|
74
|
+
end
|
79
75
|
|
80
|
-
|
81
|
-
|
82
|
-
|
76
|
+
def invisible_message
|
77
|
+
invisible_text = text(@node, :all)
|
78
|
+
invisible_count = invisible_text.scan(@search_regexp).size
|
79
|
+
if invisible_count != @count
|
80
|
+
"it was found #{invisible_count} #{Capybara::Helpers.declension("time", "times", invisible_count)} including non-visible text"
|
81
|
+
end
|
82
|
+
rescue # An error getting the non-visible text (if element goes out of scope) should not affect the response
|
83
83
|
end
|
84
84
|
|
85
85
|
def valid_keys
|
86
|
-
COUNT_KEYS + [
|
86
|
+
COUNT_KEYS + %i[wait exact]
|
87
87
|
end
|
88
88
|
|
89
89
|
def check_visible_text?
|
@@ -91,7 +91,7 @@ module Capybara
|
|
91
91
|
end
|
92
92
|
|
93
93
|
def text(node, query_type)
|
94
|
-
|
94
|
+
node.text(query_type)
|
95
95
|
end
|
96
96
|
end
|
97
97
|
end
|
@@ -1,22 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Capybara
|
3
4
|
# @api private
|
4
5
|
module Queries
|
5
6
|
class TitleQuery < BaseQuery
|
6
|
-
def initialize(expected_title, options
|
7
|
-
@expected_title = expected_title
|
7
|
+
def initialize(expected_title, **options)
|
8
|
+
@expected_title = expected_title.is_a?(Regexp) ? expected_title : expected_title.to_s
|
8
9
|
@options = options
|
9
10
|
super(@options)
|
10
|
-
|
11
|
-
@expected_title = Capybara::Helpers.normalize_whitespace(@expected_title)
|
12
|
-
end
|
13
|
-
@search_regexp = Capybara::Helpers.to_regexp(@expected_title, nil, options.fetch(:exact, false))
|
11
|
+
@search_regexp = Capybara::Helpers.to_regexp(@expected_title, all_whitespace: true, exact: options.fetch(:exact, false))
|
14
12
|
assert_valid_keys
|
15
13
|
end
|
16
14
|
|
17
15
|
def resolves_for?(node)
|
18
|
-
@actual_title = node.title
|
19
|
-
@actual_title.match(@search_regexp)
|
16
|
+
(@actual_title = node.title).match(@search_regexp)
|
20
17
|
end
|
21
18
|
|
22
19
|
def failure_message
|
@@ -27,15 +24,15 @@ module Capybara
|
|
27
24
|
failure_message_helper(' not')
|
28
25
|
end
|
29
26
|
|
30
|
-
|
27
|
+
private
|
31
28
|
|
32
29
|
def failure_message_helper(negated = '')
|
33
|
-
verb =
|
30
|
+
verb = @expected_title.is_a?(Regexp) ? 'match' : 'include'
|
34
31
|
"expected #{@actual_title.inspect}#{negated} to #{verb} #{@expected_title.inspect}"
|
35
32
|
end
|
36
33
|
|
37
34
|
def valid_keys
|
38
|
-
[
|
35
|
+
%i[wait exact]
|
39
36
|
end
|
40
37
|
end
|
41
38
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
class Capybara::RackTest::Browser
|
3
4
|
include ::Rack::Test::Methods
|
4
5
|
|
@@ -17,7 +18,7 @@ class Capybara::RackTest::Browser
|
|
17
18
|
driver.options
|
18
19
|
end
|
19
20
|
|
20
|
-
def visit(path, attributes
|
21
|
+
def visit(path, **attributes)
|
21
22
|
reset_host!
|
22
23
|
process_and_follow_redirects(:get, path, attributes)
|
23
24
|
end
|
@@ -28,23 +29,24 @@ class Capybara::RackTest::Browser
|
|
28
29
|
end
|
29
30
|
|
30
31
|
def submit(method, path, attributes)
|
31
|
-
path = request_path if
|
32
|
-
process_and_follow_redirects(method, path, attributes,
|
32
|
+
path = request_path if path.nil? || path.empty?
|
33
|
+
process_and_follow_redirects(method, path, attributes, 'HTTP_REFERER' => current_url)
|
33
34
|
end
|
34
35
|
|
35
|
-
def follow(method, path, attributes
|
36
|
+
def follow(method, path, **attributes)
|
36
37
|
return if path.gsub(/^#{Regexp.escape(request_path)}/, '').start_with?('#') || path.downcase.start_with?('javascript:')
|
37
|
-
process_and_follow_redirects(method, path, attributes,
|
38
|
+
process_and_follow_redirects(method, path, attributes, 'HTTP_REFERER' => current_url)
|
38
39
|
end
|
39
40
|
|
40
41
|
def process_and_follow_redirects(method, path, attributes = {}, env = {})
|
41
42
|
process(method, path, attributes, env)
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
43
|
+
|
44
|
+
return unless driver.follow_redirects?
|
45
|
+
|
46
|
+
driver.redirect_limit.times do
|
47
|
+
process(:get, last_response["Location"], {}, env) if last_response.redirect?
|
47
48
|
end
|
49
|
+
raise Capybara::InfiniteRedirectError, "redirected more than #{driver.redirect_limit} times, check for infinite redirects." if last_response.redirect?
|
48
50
|
end
|
49
51
|
|
50
52
|
def process(method, path, attributes = {}, env = {})
|
@@ -55,7 +57,7 @@ class Capybara::RackTest::Browser
|
|
55
57
|
else
|
56
58
|
new_uri.path = request_path if path.start_with?("?")
|
57
59
|
new_uri.path = "/" if new_uri.path.empty?
|
58
|
-
new_uri.path = request_path.sub(%r
|
60
|
+
new_uri.path = request_path.sub(%r{/[^/]*$}, '/') + new_uri.path unless new_uri.path.start_with?('/')
|
59
61
|
end
|
60
62
|
new_uri.scheme ||= @current_scheme
|
61
63
|
new_uri.host ||= @current_host
|
@@ -91,7 +93,7 @@ class Capybara::RackTest::Browser
|
|
91
93
|
end
|
92
94
|
|
93
95
|
def find(format, selector)
|
94
|
-
if format
|
96
|
+
if format == :css
|
95
97
|
dom.css(selector, Capybara::RackTest::CSSHandlers.new)
|
96
98
|
else
|
97
99
|
dom.xpath(selector)
|
@@ -105,12 +107,7 @@ class Capybara::RackTest::Browser
|
|
105
107
|
end
|
106
108
|
|
107
109
|
def title
|
108
|
-
|
109
|
-
dom.title
|
110
|
-
else
|
111
|
-
#old versions of nokogiri don't have #title - remove in 3.0
|
112
|
-
dom.xpath('/html/head/title | /html/title').first.text
|
113
|
-
end
|
110
|
+
dom.title
|
114
111
|
end
|
115
112
|
|
116
113
|
protected
|
@@ -1,11 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
class Capybara::RackTest::CSSHandlers < BasicObject
|
3
4
|
include ::Kernel
|
4
|
-
|
5
|
-
def disabled
|
5
|
+
|
6
|
+
def disabled(list)
|
6
7
|
list.find_all { |node| node.has_attribute? 'disabled' }
|
7
|
-
end
|
8
|
-
|
8
|
+
end
|
9
|
+
|
10
|
+
def enabled(list)
|
9
11
|
list.find_all { |node| !node.has_attribute? 'disabled' }
|
10
12
|
end
|
11
13
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'rack/test'
|
3
4
|
require 'rack/utils'
|
4
5
|
require 'mini_mime'
|
@@ -10,10 +11,10 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
|
|
10
11
|
respect_data_method: false,
|
11
12
|
follow_redirects: true,
|
12
13
|
redirect_limit: 5
|
13
|
-
}
|
14
|
+
}.freeze
|
14
15
|
attr_reader :app, :options
|
15
16
|
|
16
|
-
def initialize(app, options
|
17
|
+
def initialize(app, **options)
|
17
18
|
raise ArgumentError, "rack-test requires a rack application, but none was given" unless app
|
18
19
|
@session = nil
|
19
20
|
@app = app
|
@@ -40,7 +41,7 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
|
|
40
41
|
browser.last_request
|
41
42
|
end
|
42
43
|
|
43
|
-
def visit(path, attributes
|
44
|
+
def visit(path, **attributes)
|
44
45
|
browser.visit(path, attributes)
|
45
46
|
end
|
46
47
|
|
@@ -52,7 +53,7 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
|
|
52
53
|
browser.submit(method, path, attributes)
|
53
54
|
end
|
54
55
|
|
55
|
-
def follow(method, path, attributes
|
56
|
+
def follow(method, path, **attributes)
|
56
57
|
browser.follow(method, path, attributes)
|
57
58
|
end
|
58
59
|
|
@@ -73,7 +74,7 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
|
|
73
74
|
end
|
74
75
|
|
75
76
|
def find_css(selector)
|
76
|
-
browser.find(:css,selector)
|
77
|
+
browser.find(:css, selector)
|
77
78
|
end
|
78
79
|
|
79
80
|
def html
|
@@ -92,11 +93,6 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
|
|
92
93
|
@browser = nil
|
93
94
|
end
|
94
95
|
|
95
|
-
# @deprecated This method is being removed
|
96
|
-
def browser_initialized?
|
97
|
-
super && !@browser.nil?
|
98
|
-
end
|
99
|
-
|
100
96
|
def get(*args, &block); browser.get(*args, &block); end
|
101
97
|
def post(*args, &block); browser.post(*args, &block); end
|
102
98
|
def put(*args, &block); browser.put(*args, &block); end
|