capybara 3.3.1 → 3.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.md +16 -0
- data/README.md +5 -7
- data/lib/capybara.rb +7 -6
- data/lib/capybara/config.rb +1 -1
- data/lib/capybara/dsl.rb +2 -2
- data/lib/capybara/helpers.rb +3 -3
- data/lib/capybara/minitest/spec.rb +3 -3
- data/lib/capybara/node/actions.rb +18 -18
- data/lib/capybara/node/base.rb +1 -1
- data/lib/capybara/node/element.rb +2 -2
- data/lib/capybara/node/finders.rb +6 -6
- data/lib/capybara/node/matchers.rb +5 -5
- data/lib/capybara/node/simple.rb +2 -2
- data/lib/capybara/queries/ancestor_query.rb +1 -1
- data/lib/capybara/queries/base_query.rb +12 -11
- data/lib/capybara/queries/current_path_query.rb +1 -1
- data/lib/capybara/queries/selector_query.rb +39 -15
- data/lib/capybara/queries/sibling_query.rb +1 -1
- data/lib/capybara/queries/text_query.rb +1 -1
- data/lib/capybara/rack_test/browser.rb +7 -7
- data/lib/capybara/rack_test/driver.rb +1 -1
- data/lib/capybara/rack_test/form.rb +7 -7
- data/lib/capybara/rack_test/node.rb +16 -16
- data/lib/capybara/rails.rb +1 -1
- data/lib/capybara/result.rb +8 -4
- data/lib/capybara/rspec/features.rb +4 -4
- data/lib/capybara/rspec/matchers.rb +6 -6
- data/lib/capybara/selector.rb +106 -90
- data/lib/capybara/selector/css.rb +4 -4
- data/lib/capybara/selector/filter_set.rb +52 -8
- data/lib/capybara/selector/selector.rb +39 -15
- data/lib/capybara/selenium/driver.rb +10 -10
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +8 -0
- data/lib/capybara/selenium/node.rb +9 -10
- data/lib/capybara/selenium/nodes/chrome_node.rb +18 -0
- data/lib/capybara/selenium/nodes/marionette_node.rb +32 -7
- data/lib/capybara/server.rb +3 -3
- data/lib/capybara/server/animation_disabler.rb +1 -1
- data/lib/capybara/server/middleware.rb +1 -1
- data/lib/capybara/session.rb +23 -19
- data/lib/capybara/session/config.rb +18 -3
- data/lib/capybara/spec/public/test.js +1 -1
- data/lib/capybara/spec/session/accept_alert_spec.rb +10 -10
- data/lib/capybara/spec/session/accept_confirm_spec.rb +3 -3
- data/lib/capybara/spec/session/accept_prompt_spec.rb +9 -10
- data/lib/capybara/spec/session/all_spec.rb +33 -32
- data/lib/capybara/spec/session/ancestor_spec.rb +19 -19
- data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +38 -38
- data/lib/capybara/spec/session/assert_current_path_spec.rb +16 -16
- data/lib/capybara/spec/session/assert_selector_spec.rb +53 -53
- data/lib/capybara/spec/session/assert_style_spec.rb +3 -3
- data/lib/capybara/spec/session/assert_text_spec.rb +31 -30
- data/lib/capybara/spec/session/assert_title_spec.rb +12 -12
- data/lib/capybara/spec/session/attach_file_spec.rb +51 -52
- data/lib/capybara/spec/session/body_spec.rb +6 -6
- data/lib/capybara/spec/session/check_spec.rb +52 -47
- data/lib/capybara/spec/session/choose_spec.rb +32 -32
- data/lib/capybara/spec/session/click_button_spec.rb +103 -103
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +24 -23
- data/lib/capybara/spec/session/click_link_spec.rb +49 -48
- data/lib/capybara/spec/session/current_scope_spec.rb +7 -7
- data/lib/capybara/spec/session/current_url_spec.rb +26 -27
- data/lib/capybara/spec/session/dismiss_confirm_spec.rb +3 -3
- data/lib/capybara/spec/session/dismiss_prompt_spec.rb +2 -2
- data/lib/capybara/spec/session/element/assert_match_selector_spec.rb +8 -8
- data/lib/capybara/spec/session/element/match_css_spec.rb +10 -10
- data/lib/capybara/spec/session/element/match_xpath_spec.rb +6 -6
- data/lib/capybara/spec/session/element/matches_selector_spec.rb +51 -51
- data/lib/capybara/spec/session/evaluate_async_script_spec.rb +7 -7
- data/lib/capybara/spec/session/evaluate_script_spec.rb +15 -8
- data/lib/capybara/spec/session/execute_script_spec.rb +7 -7
- data/lib/capybara/spec/session/fill_in_spec.rb +43 -42
- data/lib/capybara/spec/session/find_button_spec.rb +23 -23
- data/lib/capybara/spec/session/find_by_id_spec.rb +7 -7
- data/lib/capybara/spec/session/find_field_spec.rb +32 -30
- data/lib/capybara/spec/session/find_link_spec.rb +21 -21
- data/lib/capybara/spec/session/find_spec.rb +153 -135
- data/lib/capybara/spec/session/first_spec.rb +41 -41
- data/lib/capybara/spec/session/frame/frame_title_spec.rb +5 -5
- data/lib/capybara/spec/session/frame/frame_url_spec.rb +5 -5
- data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +17 -17
- data/lib/capybara/spec/session/frame/within_frame_spec.rb +31 -17
- data/lib/capybara/spec/session/go_back_spec.rb +1 -1
- data/lib/capybara/spec/session/go_forward_spec.rb +1 -1
- data/lib/capybara/spec/session/has_all_selectors_spec.rb +17 -17
- data/lib/capybara/spec/session/has_button_spec.rb +13 -13
- data/lib/capybara/spec/session/has_css_spec.rb +133 -131
- data/lib/capybara/spec/session/has_current_path_spec.rb +29 -29
- data/lib/capybara/spec/session/has_field_spec.rb +58 -58
- data/lib/capybara/spec/session/has_link_spec.rb +4 -4
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +24 -24
- data/lib/capybara/spec/session/has_select_spec.rb +43 -43
- data/lib/capybara/spec/session/has_selector_spec.rb +71 -71
- data/lib/capybara/spec/session/has_style_spec.rb +3 -3
- data/lib/capybara/spec/session/has_table_spec.rb +4 -4
- data/lib/capybara/spec/session/has_text_spec.rb +53 -52
- data/lib/capybara/spec/session/has_title_spec.rb +14 -14
- data/lib/capybara/spec/session/has_xpath_spec.rb +39 -38
- data/lib/capybara/spec/session/headers_spec.rb +1 -1
- data/lib/capybara/spec/session/html_spec.rb +6 -6
- data/lib/capybara/spec/session/node_spec.rb +129 -123
- data/lib/capybara/spec/session/node_wrapper_spec.rb +10 -7
- data/lib/capybara/spec/session/refresh_spec.rb +4 -7
- data/lib/capybara/spec/session/reset_session_spec.rb +28 -28
- data/lib/capybara/spec/session/response_code_spec.rb +1 -1
- data/lib/capybara/spec/session/save_and_open_page_spec.rb +2 -2
- data/lib/capybara/spec/session/save_page_spec.rb +37 -37
- data/lib/capybara/spec/session/save_screenshot_spec.rb +6 -6
- data/lib/capybara/spec/session/screenshot_spec.rb +2 -2
- data/lib/capybara/spec/session/select_spec.rb +81 -81
- data/lib/capybara/spec/session/selectors_spec.rb +17 -17
- data/lib/capybara/spec/session/sibling_spec.rb +9 -9
- data/lib/capybara/spec/session/text_spec.rb +23 -23
- data/lib/capybara/spec/session/title_spec.rb +5 -5
- data/lib/capybara/spec/session/uncheck_spec.rb +24 -20
- data/lib/capybara/spec/session/unselect_spec.rb +37 -37
- data/lib/capybara/spec/session/visit_spec.rb +48 -49
- data/lib/capybara/spec/session/window/current_window_spec.rb +1 -1
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +16 -16
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +2 -2
- data/lib/capybara/spec/session/window/window_spec.rb +4 -4
- data/lib/capybara/spec/session/window/within_window_spec.rb +14 -14
- data/lib/capybara/spec/session/within_spec.rb +41 -41
- data/lib/capybara/spec/spec_helper.rb +11 -9
- data/lib/capybara/spec/test_app.rb +18 -17
- data/lib/capybara/spec/views/form.erb +29 -31
- data/lib/capybara/spec/views/with_html.erb +2 -2
- data/lib/capybara/version.rb +1 -1
- data/spec/basic_node_spec.rb +23 -23
- data/spec/capybara_spec.rb +20 -20
- data/spec/css_splitter_spec.rb +7 -7
- data/spec/dsl_spec.rb +37 -32
- data/spec/filter_set_spec.rb +4 -4
- data/spec/fixtures/selenium_driver_rspec_failure.rb +1 -1
- data/spec/fixtures/selenium_driver_rspec_success.rb +1 -1
- data/spec/minitest_spec.rb +4 -4
- data/spec/minitest_spec_spec.rb +23 -23
- data/spec/per_session_config_spec.rb +5 -5
- data/spec/rack_test_spec.rb +44 -44
- data/spec/result_spec.rb +14 -14
- data/spec/rspec/features_spec.rb +13 -13
- data/spec/rspec/scenarios_spec.rb +4 -4
- data/spec/rspec/shared_spec_matchers.rb +282 -281
- data/spec/rspec/views_spec.rb +3 -3
- data/spec/rspec_matchers_spec.rb +10 -10
- data/spec/rspec_spec.rb +29 -29
- data/spec/selector_spec.rb +64 -64
- data/spec/selenium_spec_chrome.rb +14 -22
- data/spec/selenium_spec_chrome_remote.rb +28 -8
- data/spec/selenium_spec_edge.rb +9 -4
- data/spec/selenium_spec_firefox_remote.rb +87 -0
- data/spec/selenium_spec_ie.rb +9 -4
- data/spec/selenium_spec_marionette.rb +42 -18
- data/spec/server_spec.rb +29 -27
- data/spec/session_spec.rb +17 -17
- data/spec/shared_selenium_session.rb +70 -52
- data/spec/spec_helper.rb +1 -1
- metadata +4 -2
data/lib/capybara/selector.rb
CHANGED
@@ -10,15 +10,15 @@ Capybara::Selector::FilterSet.add(:_field) do
|
|
10
10
|
expression_filter(:name) { |xpath, val| xpath[XPath.attr(:name) == val] }
|
11
11
|
expression_filter(:placeholder) { |xpath, val| xpath[XPath.attr(:placeholder) == val] }
|
12
12
|
|
13
|
-
describe do |checked: nil, unchecked: nil, disabled: nil, multiple: nil,
|
14
|
-
desc, states = +
|
13
|
+
describe(:node_filters) do |checked: nil, unchecked: nil, disabled: nil, multiple: nil, **|
|
14
|
+
desc, states = +'', []
|
15
15
|
states << 'checked' if checked || (unchecked == false)
|
16
16
|
states << 'not checked' if unchecked || (checked == false)
|
17
17
|
states << 'disabled' if disabled == true
|
18
18
|
states << 'not disabled' if disabled == false
|
19
19
|
desc << " that is #{states.join(' and ')}" unless states.empty?
|
20
|
-
desc <<
|
21
|
-
desc <<
|
20
|
+
desc << ' with the multiple attribute' if multiple == true
|
21
|
+
desc << ' without the multiple attribute' if multiple == false
|
22
22
|
desc
|
23
23
|
end
|
24
24
|
end
|
@@ -58,26 +58,34 @@ Capybara.add_selector(:field) do
|
|
58
58
|
node_filter(:with) do |node, with|
|
59
59
|
with.is_a?(Regexp) ? node.value =~ with : node.value == with.to_s
|
60
60
|
end
|
61
|
-
|
62
|
-
|
61
|
+
|
62
|
+
describe_expression_filters do |type: nil, **options|
|
63
|
+
desc = +''
|
63
64
|
(expression_filters.keys - [:type]).each { |ef| desc << " with #{ef} #{options[ef]}" if options.key?(ef) }
|
64
65
|
desc << " of type #{type.inspect}" if type
|
65
|
-
desc << " with value #{options[:with].to_s.inspect}" if options.key?(:with)
|
66
66
|
desc
|
67
67
|
end
|
68
|
+
|
69
|
+
describe_node_filters do |**options|
|
70
|
+
" with value #{options[:with].to_s.inspect}" if options.key?(:with)
|
71
|
+
end
|
68
72
|
end
|
69
73
|
|
70
74
|
Capybara.add_selector(:fieldset) do
|
71
|
-
xpath(:legend) do |locator, legend: nil,
|
75
|
+
xpath(:legend) do |locator, legend: nil, **|
|
76
|
+
locator_matchers = (XPath.attr(:id) == locator.to_s) | XPath.child(:legend)[XPath.string.n.is(locator.to_s)]
|
77
|
+
locator_matchers |= XPath.attr(test_id) == locator if test_id
|
72
78
|
xpath = XPath.descendant(:fieldset)
|
73
|
-
xpath = xpath[
|
79
|
+
xpath = xpath[locator_matchers] unless locator.nil?
|
74
80
|
xpath = xpath[XPath.child(:legend)[XPath.string.n.is(legend)]] if legend
|
75
81
|
xpath
|
76
82
|
end
|
83
|
+
|
84
|
+
node_filter(:disabled, :boolean) { |node, value| !(value ^ node.disabled?) }
|
77
85
|
end
|
78
86
|
|
79
87
|
Capybara.add_selector(:link) do
|
80
|
-
xpath(:title, :alt) do |locator, href: true,
|
88
|
+
xpath(:title, :alt) do |locator, href: true, alt: nil, title: nil, **|
|
81
89
|
xpath = XPath.descendant(:a)
|
82
90
|
xpath = xpath[
|
83
91
|
case href
|
@@ -99,6 +107,7 @@ Capybara.add_selector(:link) do
|
|
99
107
|
XPath.attr(:title).is(locator),
|
100
108
|
XPath.descendant(:img)[XPath.attr(:alt).is(locator)]]
|
101
109
|
matchers << XPath.attr(:'aria-label').is(locator) if enable_aria_label
|
110
|
+
matchers << XPath.attr(test_id) == locator if test_id
|
102
111
|
xpath = xpath[matchers.reduce(:|)]
|
103
112
|
end
|
104
113
|
|
@@ -121,27 +130,33 @@ Capybara.add_selector(:link) do
|
|
121
130
|
expr[mod]
|
122
131
|
end
|
123
132
|
|
124
|
-
|
125
|
-
desc = +
|
126
|
-
desc << " with href #{options[:href].inspect}" if options[:href]
|
127
|
-
desc <<
|
133
|
+
describe_expression_filters do |**options|
|
134
|
+
desc = +''
|
135
|
+
desc << " with href #{options[:href].inspect}" if options[:href] && !options[:href].is_a?(Regexp)
|
136
|
+
desc << ' with no href attribute' if options.fetch(:href, true).nil?
|
137
|
+
desc
|
138
|
+
end
|
139
|
+
|
140
|
+
describe_node_filters do |href: nil, **|
|
141
|
+
" with href matching #{href.inspect}" if href.is_a? Regexp
|
128
142
|
end
|
129
143
|
end
|
130
144
|
|
131
145
|
Capybara.add_selector(:button) do
|
132
|
-
xpath(:value, :title, :type) do |locator,
|
146
|
+
xpath(:value, :title, :type) do |locator, **options|
|
133
147
|
input_btn_xpath = XPath.descendant(:input)[XPath.attr(:type).one_of('submit', 'reset', 'image', 'button')]
|
134
148
|
btn_xpath = XPath.descendant(:button)
|
135
149
|
image_btn_xpath = XPath.descendant(:input)[XPath.attr(:type) == 'image']
|
136
150
|
|
137
151
|
unless locator.nil?
|
138
152
|
locator = locator.to_s
|
139
|
-
|
140
|
-
|
153
|
+
locator_matchers = XPath.attr(:id).equals(locator) | XPath.attr(:value).is(locator) | XPath.attr(:title).is(locator)
|
154
|
+
locator_matchers |= XPath.attr(:'aria-label').is(locator) if enable_aria_label
|
155
|
+
locator_matchers |= XPath.attr(test_id) == locator if test_id
|
141
156
|
|
142
|
-
input_btn_xpath = input_btn_xpath[
|
157
|
+
input_btn_xpath = input_btn_xpath[locator_matchers]
|
143
158
|
|
144
|
-
btn_xpath = btn_xpath[
|
159
|
+
btn_xpath = btn_xpath[locator_matchers | XPath.string.n.is(locator) | XPath.descendant(:img)[XPath.attr(:alt).is(locator)]]
|
145
160
|
|
146
161
|
alt_matches = XPath.attr(:alt).is(locator)
|
147
162
|
alt_matches |= XPath.attr(:'aria-label').is(locator) if enable_aria_label
|
@@ -157,27 +172,27 @@ Capybara.add_selector(:button) do
|
|
157
172
|
|
158
173
|
node_filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| !(value ^ node.disabled?) }
|
159
174
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
desc << describe_all_expression_filters(options)
|
164
|
-
desc
|
175
|
+
describe_expression_filters
|
176
|
+
describe_node_filters do |disabled: nil, **|
|
177
|
+
' that is disabled' if disabled == true
|
165
178
|
end
|
166
179
|
end
|
167
180
|
|
168
181
|
Capybara.add_selector(:link_or_button) do
|
169
|
-
label
|
182
|
+
label 'link or button'
|
170
183
|
xpath do |locator, **options|
|
171
184
|
self.class.all.values_at(:link, :button).map { |selector| selector.xpath.call(locator, options) }.reduce(:union)
|
172
185
|
end
|
173
186
|
|
174
|
-
node_filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| node.tag_name ==
|
187
|
+
node_filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| node.tag_name == 'a' || !(value ^ node.disabled?) }
|
175
188
|
|
176
|
-
|
189
|
+
describe_node_filters do |disabled: nil, **|
|
190
|
+
' that is disabled' if disabled == true
|
191
|
+
end
|
177
192
|
end
|
178
193
|
|
179
194
|
Capybara.add_selector(:fillable_field) do
|
180
|
-
label
|
195
|
+
label 'field'
|
181
196
|
|
182
197
|
xpath do |locator, **options|
|
183
198
|
xpath = XPath.descendant(:input, :textarea)[!XPath.attr(:type).one_of('submit', 'image', 'radio', 'checkbox', 'hidden', 'file')]
|
@@ -186,7 +201,7 @@ Capybara.add_selector(:fillable_field) do
|
|
186
201
|
|
187
202
|
expression_filter(:type) do |expr, type|
|
188
203
|
type = type.to_s
|
189
|
-
if
|
204
|
+
if type == 'textarea'
|
190
205
|
expr.self(type.to_sym)
|
191
206
|
else
|
192
207
|
expr[XPath.attr(:type) == type]
|
@@ -199,16 +214,14 @@ Capybara.add_selector(:fillable_field) do
|
|
199
214
|
with.is_a?(Regexp) ? node.value =~ with : node.value == with.to_s
|
200
215
|
end
|
201
216
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
desc << " with value #{options[:with].to_s.inspect}" if options.key?(:with)
|
206
|
-
desc
|
217
|
+
describe_expression_filters
|
218
|
+
describe_node_filters do |**options|
|
219
|
+
" with value #{options[:with].to_s.inspect}" if options.key?(:with)
|
207
220
|
end
|
208
221
|
end
|
209
222
|
|
210
223
|
Capybara.add_selector(:radio_button) do
|
211
|
-
label
|
224
|
+
label 'radio button'
|
212
225
|
|
213
226
|
xpath do |locator, **options|
|
214
227
|
xpath = XPath.descendant(:input)[XPath.attr(:type) == 'radio']
|
@@ -219,11 +232,9 @@ Capybara.add_selector(:radio_button) do
|
|
219
232
|
|
220
233
|
node_filter(:option) { |node, value| node.value == value.to_s }
|
221
234
|
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
desc << describe_all_expression_filters(options)
|
226
|
-
desc
|
235
|
+
describe_expression_filters
|
236
|
+
describe_node_filters do |option: nil, **|
|
237
|
+
" with value #{option.inspect}" if option
|
227
238
|
end
|
228
239
|
end
|
229
240
|
|
@@ -237,16 +248,14 @@ Capybara.add_selector(:checkbox) do
|
|
237
248
|
|
238
249
|
node_filter(:option) { |node, value| node.value == value.to_s }
|
239
250
|
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
desc << describe_all_expression_filters(options)
|
244
|
-
desc
|
251
|
+
describe_expression_filters
|
252
|
+
describe_node_filters do |option: nil, **|
|
253
|
+
" with value #{option.inspect}" if option
|
245
254
|
end
|
246
255
|
end
|
247
256
|
|
248
257
|
Capybara.add_selector(:select) do
|
249
|
-
label
|
258
|
+
label 'select box'
|
250
259
|
|
251
260
|
xpath do |locator, **options|
|
252
261
|
xpath = XPath.descendant(:select)
|
@@ -280,19 +289,24 @@ Capybara.add_selector(:select) do
|
|
280
289
|
(Array(selected) - actual).empty?
|
281
290
|
end
|
282
291
|
|
283
|
-
|
284
|
-
desc = +
|
285
|
-
desc << " with options #{options.inspect}" if options
|
292
|
+
describe_expression_filters do |with_options: nil, **opts|
|
293
|
+
desc = +''
|
286
294
|
desc << " with at least options #{with_options.inspect}" if with_options
|
295
|
+
desc << describe_all_expression_filters(opts)
|
296
|
+
desc
|
297
|
+
end
|
298
|
+
|
299
|
+
describe_node_filters do |options: nil, selected: nil, with_selected: nil, **|
|
300
|
+
desc = +''
|
301
|
+
desc << " with options #{options.inspect}" if options
|
287
302
|
desc << " with #{selected.inspect} selected" if selected
|
288
303
|
desc << " with at least #{with_selected.inspect} selected" if with_selected
|
289
|
-
desc << describe_all_expression_filters(opts)
|
290
304
|
desc
|
291
305
|
end
|
292
306
|
end
|
293
307
|
|
294
308
|
Capybara.add_selector(:datalist_input) do
|
295
|
-
label
|
309
|
+
label 'input box with datalist completion'
|
296
310
|
|
297
311
|
xpath do |locator, **options|
|
298
312
|
xpath = XPath.descendant(:input)[XPath.attr(:list)]
|
@@ -312,13 +326,16 @@ Capybara.add_selector(:datalist_input) do
|
|
312
326
|
end
|
313
327
|
end
|
314
328
|
|
315
|
-
|
316
|
-
desc = +
|
317
|
-
desc << " with options #{options.inspect}" if options
|
329
|
+
describe_expression_filters do |with_options: nil, **opts|
|
330
|
+
desc = +''
|
318
331
|
desc << " with at least options #{with_options.inspect}" if with_options
|
319
332
|
desc << describe_all_expression_filters(opts)
|
320
333
|
desc
|
321
334
|
end
|
335
|
+
|
336
|
+
describe_node_filters do |options: nil, **|
|
337
|
+
" with options #{options.inspect}" if options
|
338
|
+
end
|
322
339
|
end
|
323
340
|
|
324
341
|
Capybara.add_selector(:option) do
|
@@ -331,8 +348,8 @@ Capybara.add_selector(:option) do
|
|
331
348
|
node_filter(:disabled, :boolean) { |node, value| !(value ^ node.disabled?) }
|
332
349
|
node_filter(:selected, :boolean) { |node, value| !(value ^ node.selected?) }
|
333
350
|
|
334
|
-
|
335
|
-
desc = +
|
351
|
+
describe_node_filters do |**options|
|
352
|
+
desc = +''
|
336
353
|
desc << " that is#{' not' unless options[:disabled]} disabled" if options.key?(:disabled)
|
337
354
|
desc << " that is#{' not' unless options[:selected]} selected" if options.key?(:selected)
|
338
355
|
desc
|
@@ -340,7 +357,7 @@ Capybara.add_selector(:option) do
|
|
340
357
|
end
|
341
358
|
|
342
359
|
Capybara.add_selector(:datalist_option) do
|
343
|
-
label
|
360
|
+
label 'datalist option'
|
344
361
|
visible(:all)
|
345
362
|
|
346
363
|
xpath do |locator|
|
@@ -351,15 +368,13 @@ Capybara.add_selector(:datalist_option) do
|
|
351
368
|
|
352
369
|
node_filter(:disabled, :boolean) { |node, value| !(value ^ node.disabled?) }
|
353
370
|
|
354
|
-
|
355
|
-
|
356
|
-
desc << " that is#{' not' unless options[:disabled]} disabled" if options.key?(:disabled)
|
357
|
-
desc
|
371
|
+
describe_node_filters do |**options|
|
372
|
+
" that is#{' not' unless options[:disabled]} disabled" if options.key?(:disabled)
|
358
373
|
end
|
359
374
|
end
|
360
375
|
|
361
376
|
Capybara.add_selector(:file_field) do
|
362
|
-
label
|
377
|
+
label 'file field'
|
363
378
|
xpath do |locator, options|
|
364
379
|
xpath = XPath.descendant(:input)[XPath.attr(:type) == 'file']
|
365
380
|
locate_field(xpath, locator, options)
|
@@ -367,18 +382,18 @@ Capybara.add_selector(:file_field) do
|
|
367
382
|
|
368
383
|
filter_set(:_field, %i[disabled multiple name])
|
369
384
|
|
370
|
-
|
371
|
-
desc = +""
|
372
|
-
desc << describe_all_expression_filters(options)
|
373
|
-
desc
|
374
|
-
end
|
385
|
+
describe_expression_filters
|
375
386
|
end
|
376
387
|
|
377
388
|
Capybara.add_selector(:label) do
|
378
|
-
label
|
389
|
+
label 'label'
|
379
390
|
xpath(:for) do |locator, options|
|
380
391
|
xpath = XPath.descendant(:label)
|
381
|
-
|
392
|
+
unless locator.nil?
|
393
|
+
locator_matchers = XPath.string.n.is(locator.to_s) | (XPath.attr(:id) == locator.to_s)
|
394
|
+
locator_matchers |= XPath.attr(test_id) == locator if test_id
|
395
|
+
xpath = xpath[locator_matchers]
|
396
|
+
end
|
382
397
|
if options.key?(:for) && !options[:for].is_a?(Capybara::Node::Element)
|
383
398
|
with_attr = XPath.attr(:for) == options[:for].to_s
|
384
399
|
labelable_elements = %i[button input keygen meter output progress select textarea]
|
@@ -401,45 +416,50 @@ Capybara.add_selector(:label) do
|
|
401
416
|
end
|
402
417
|
end
|
403
418
|
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
419
|
+
describe_expression_filters do |**options|
|
420
|
+
" for element with id of \"#{options[:for]}\"" if options.key?(:for) && !options[:for].is_a?(Capybara::Node::Element)
|
421
|
+
end
|
422
|
+
describe_node_filters do |**options|
|
423
|
+
" for element #{options[:for]}" if options[:for]&.is_a?(Capybara::Node::Element)
|
408
424
|
end
|
409
425
|
end
|
410
426
|
|
411
427
|
Capybara.add_selector(:table) do
|
412
|
-
xpath(:caption) do |locator, caption: nil,
|
428
|
+
xpath(:caption) do |locator, caption: nil, **|
|
413
429
|
xpath = XPath.descendant(:table)
|
414
|
-
|
430
|
+
unless locator.nil?
|
431
|
+
locator_matchers = (XPath.attr(:id) == locator.to_s) | XPath.descendant(:caption).is(locator.to_s)
|
432
|
+
locator_matchers |= XPath.attr(test_id) == locator if test_id
|
433
|
+
xpath = xpath[locator_matchers]
|
434
|
+
end
|
415
435
|
xpath = xpath[XPath.descendant(:caption) == caption] if caption
|
416
436
|
xpath
|
417
437
|
end
|
418
438
|
|
419
|
-
|
420
|
-
|
421
|
-
desc << " with caption #{caption}" if caption
|
422
|
-
desc
|
439
|
+
describe_expression_filters do |caption: nil, **|
|
440
|
+
" with caption \"#{caption}\"" if caption
|
423
441
|
end
|
424
442
|
end
|
425
443
|
|
426
444
|
Capybara.add_selector(:frame) do
|
427
445
|
xpath(:name) do |locator, **options|
|
428
446
|
xpath = XPath.descendant(:iframe).union(XPath.descendant(:frame))
|
429
|
-
|
447
|
+
unless locator.nil?
|
448
|
+
locator_matchers = (XPath.attr(:id) == locator.to_s) | (XPath.attr(:name) == locator.to_s)
|
449
|
+
locator_matchers |= XPath.attr(test_id) == locator if test_id
|
450
|
+
xpath = xpath[locator_matchers]
|
451
|
+
end
|
430
452
|
xpath = expression_filters.keys.inject(xpath) { |memo, ef| memo[find_by_attr(ef, options[ef])] }
|
431
453
|
xpath
|
432
454
|
end
|
433
455
|
|
434
|
-
|
435
|
-
|
436
|
-
desc << " with name #{name}" if name
|
437
|
-
desc
|
456
|
+
describe_expression_filters do |name: nil, **|
|
457
|
+
" with name #{name}" if name
|
438
458
|
end
|
439
459
|
end
|
440
460
|
|
441
461
|
Capybara.add_selector(:element) do
|
442
|
-
xpath do |locator,
|
462
|
+
xpath do |locator, **|
|
443
463
|
XPath.descendant((locator || '@').to_sym)
|
444
464
|
end
|
445
465
|
|
@@ -458,10 +478,6 @@ Capybara.add_selector(:element) do
|
|
458
478
|
val.is_a?(Regexp) ? node[name] =~ val : true
|
459
479
|
end
|
460
480
|
|
461
|
-
|
462
|
-
desc = +""
|
463
|
-
desc << describe_all_expression_filters(options)
|
464
|
-
desc
|
465
|
-
end
|
481
|
+
describe_expression_filters
|
466
482
|
end
|
467
483
|
# rubocop:enable Metrics/BlockLength
|
@@ -5,7 +5,7 @@ module Capybara
|
|
5
5
|
class CSS
|
6
6
|
def self.escape(str)
|
7
7
|
value = str.dup
|
8
|
-
out = +
|
8
|
+
out = +''
|
9
9
|
out << value.slice!(0...1) if value =~ /^[-_]/
|
10
10
|
out << (value[0] =~ NMSTART ? value.slice!(0...1) : escape_char(value.slice!(0...1)))
|
11
11
|
out << value.gsub(/[^a-zA-Z0-9_-]/) { |c| escape_char c }
|
@@ -13,7 +13,7 @@ module Capybara
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def self.escape_char(c)
|
16
|
-
c =~ %r{[ -/:-~]} ? "\\#{c}" : format(
|
16
|
+
c =~ %r{[ -/:-~]} ? "\\#{c}" : format('\\%06x', c.ord)
|
17
17
|
end
|
18
18
|
|
19
19
|
def self.split(css)
|
@@ -31,7 +31,7 @@ module Capybara
|
|
31
31
|
def split(css)
|
32
32
|
selectors = []
|
33
33
|
StringIO.open(css) do |str|
|
34
|
-
selector =
|
34
|
+
selector = ''
|
35
35
|
while (c = str.getc)
|
36
36
|
case c
|
37
37
|
when '['
|
@@ -44,7 +44,7 @@ module Capybara
|
|
44
44
|
selector += c + str.getc
|
45
45
|
when ','
|
46
46
|
selectors << selector.strip
|
47
|
-
selector =
|
47
|
+
selector = ''
|
48
48
|
else
|
49
49
|
selector += c
|
50
50
|
end
|
@@ -5,13 +5,13 @@ require 'capybara/selector/filter'
|
|
5
5
|
module Capybara
|
6
6
|
class Selector
|
7
7
|
class FilterSet
|
8
|
-
attr_reader :
|
8
|
+
attr_reader :node_filters, :expression_filters
|
9
9
|
|
10
10
|
def initialize(name, &block)
|
11
11
|
@name = name
|
12
|
-
@descriptions = []
|
13
|
-
@expression_filters = {}
|
14
12
|
@node_filters = {}
|
13
|
+
@expression_filters = {}
|
14
|
+
@descriptions = Hash.new { |h, k| h[k] = [] }
|
15
15
|
instance_eval(&block)
|
16
16
|
end
|
17
17
|
|
@@ -24,13 +24,43 @@ module Capybara
|
|
24
24
|
add_filter(name, Filters::ExpressionFilter, *types_and_options, &block)
|
25
25
|
end
|
26
26
|
|
27
|
-
def describe(&block)
|
28
|
-
|
27
|
+
def describe(what = nil, &block)
|
28
|
+
case what
|
29
|
+
when nil
|
30
|
+
undeclared_descriptions.push block
|
31
|
+
when :node_filters
|
32
|
+
node_filter_descriptions.push block
|
33
|
+
when :expression_filters
|
34
|
+
expression_filter_descriptions.push block
|
35
|
+
else
|
36
|
+
raise ArgumentError, 'Unknown description type'
|
37
|
+
end
|
29
38
|
end
|
30
39
|
|
31
|
-
def description(**options)
|
40
|
+
def description(node_filters: true, expression_filters: true, **options)
|
32
41
|
opts = options_with_defaults(options)
|
33
|
-
|
42
|
+
d = +''
|
43
|
+
d += undeclared_descriptions.map { |desc| desc.call(opts).to_s }.join
|
44
|
+
d += expression_filter_descriptions.map { |desc| desc.call(opts).to_s }.join if expression_filters
|
45
|
+
d += node_filter_descriptions.map { |desc| desc.call(opts).to_s }.join if node_filters
|
46
|
+
d
|
47
|
+
end
|
48
|
+
|
49
|
+
def descriptions
|
50
|
+
warn 'DEPRECATED: FilterSet#descriptions is deprecated without replacement'
|
51
|
+
[undeclared_descriptions, node_filter_descriptions, expression_filter_descriptions].flatten
|
52
|
+
end
|
53
|
+
|
54
|
+
def import(name, filters = nil)
|
55
|
+
f_set = self.class.all[name]
|
56
|
+
filter_selector = filters.nil? ? ->(*) { true } : ->(n, _) { filters.include? n }
|
57
|
+
|
58
|
+
expression_filters.merge!(f_set.expression_filters.select(&filter_selector))
|
59
|
+
node_filters.merge!(f_set.node_filters.select(&filter_selector))
|
60
|
+
|
61
|
+
f_set.undeclared_descriptions.each { |desc| describe(&desc) }
|
62
|
+
f_set.expression_filter_descriptions.each { |desc| describe(:expression_filters, &desc) }
|
63
|
+
f_set.node_filter_descriptions.each { |desc| describe(:node_filters, &desc) }
|
34
64
|
end
|
35
65
|
|
36
66
|
class << self
|
@@ -47,6 +77,20 @@ module Capybara
|
|
47
77
|
end
|
48
78
|
end
|
49
79
|
|
80
|
+
protected
|
81
|
+
|
82
|
+
def undeclared_descriptions
|
83
|
+
@descriptions[:undeclared]
|
84
|
+
end
|
85
|
+
|
86
|
+
def node_filter_descriptions
|
87
|
+
@descriptions[:node_filters]
|
88
|
+
end
|
89
|
+
|
90
|
+
def expression_filter_descriptions
|
91
|
+
@descriptions[:expression_filters]
|
92
|
+
end
|
93
|
+
|
50
94
|
private
|
51
95
|
|
52
96
|
def options_with_defaults(options)
|
@@ -61,7 +105,7 @@ module Capybara
|
|
61
105
|
|
62
106
|
def add_filter(name, filter_class, *types, matcher: nil, **options, &block)
|
63
107
|
types.each { |k| options[k] = true }
|
64
|
-
raise
|
108
|
+
raise 'ArgumentError', ':default option is not supported for filters with a :matcher option' if matcher && options[:default]
|
65
109
|
if filter_class <= Filters::ExpressionFilter
|
66
110
|
@expression_filters[name] = filter_class.new(name, matcher, block, options)
|
67
111
|
else
|