capybara 2.14.0 → 2.14.1
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 +12 -0
- data/README.md +1 -1
- data/lib/capybara.rb +16 -15
- data/lib/capybara/minitest.rb +92 -113
- data/lib/capybara/minitest/spec.rb +12 -37
- data/lib/capybara/node/matchers.rb +6 -5
- data/lib/capybara/queries/base_query.rb +4 -0
- data/lib/capybara/queries/current_path_query.rb +1 -0
- data/lib/capybara/queries/selector_query.rb +39 -12
- data/lib/capybara/queries/text_query.rb +1 -1
- data/lib/capybara/queries/title_query.rb +1 -0
- data/lib/capybara/rack_test/driver.rb +1 -0
- data/lib/capybara/rspec/matcher_proxies.rb +10 -6
- data/lib/capybara/rspec/matchers.rb +29 -0
- data/lib/capybara/selector.rb +61 -50
- data/lib/capybara/selector/expression_filter.rb +40 -0
- data/lib/capybara/selector/filter_set.rb +22 -3
- data/lib/capybara/selector/selector.rb +33 -12
- data/lib/capybara/selenium/driver.rb +130 -25
- data/lib/capybara/selenium/node.rb +3 -3
- data/lib/capybara/session/config.rb +29 -23
- data/lib/capybara/session/matchers.rb +3 -0
- data/lib/capybara/spec/session/accept_alert_spec.rb +1 -1
- data/lib/capybara/spec/session/all_spec.rb +2 -1
- data/lib/capybara/spec/session/assert_selector.rb +1 -1
- data/lib/capybara/spec/session/assert_title.rb +22 -9
- data/lib/capybara/spec/session/dismiss_confirm_spec.rb +3 -3
- data/lib/capybara/spec/session/dismiss_prompt_spec.rb +1 -1
- data/lib/capybara/spec/session/find_spec.rb +3 -2
- data/lib/capybara/spec/session/first_spec.rb +10 -5
- data/lib/capybara/spec/session/has_css_spec.rb +11 -0
- data/lib/capybara/spec/session/has_current_path_spec.rb +5 -3
- data/lib/capybara/spec/session/has_select_spec.rb +62 -4
- data/lib/capybara/spec/session/has_text_spec.rb +5 -3
- data/lib/capybara/spec/session/has_title_spec.rb +4 -2
- data/lib/capybara/spec/session/has_xpath_spec.rb +5 -3
- data/lib/capybara/spec/session/node_spec.rb +8 -4
- data/lib/capybara/spec/session/window/become_closed_spec.rb +4 -4
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +4 -4
- data/lib/capybara/spec/spec_helper.rb +1 -1
- data/lib/capybara/spec/views/form.erb +22 -1
- data/lib/capybara/spec/views/with_html.erb +1 -1
- data/lib/capybara/version.rb +1 -1
- data/spec/capybara_spec.rb +16 -0
- data/spec/filter_set_spec.rb +28 -0
- data/spec/minitest_spec_spec.rb +4 -4
- data/spec/per_session_config_spec.rb +4 -4
- data/spec/result_spec.rb +20 -0
- data/spec/selector_spec.rb +2 -1
- data/spec/selenium_spec_chrome.rb +12 -1
- data/spec/selenium_spec_firefox.rb +2 -1
- data/spec/selenium_spec_marionette.rb +4 -3
- data/spec/shared_selenium_session.rb +14 -7
- metadata +18 -2
@@ -9,7 +9,7 @@ module Capybara
|
|
9
9
|
|
10
10
|
def initialize(*args, &filter_block)
|
11
11
|
@options = if args.last.is_a?(Hash) then args.pop.dup else {} end
|
12
|
-
|
12
|
+
super(@options)
|
13
13
|
|
14
14
|
@filter_block = filter_block
|
15
15
|
|
@@ -47,7 +47,7 @@ module Capybara
|
|
47
47
|
@description << " with#{" exact" if exact_text == true} text #{options[:text].inspect}" if options[:text]
|
48
48
|
@description << " with exact text #{options[:exact_text]}" if options[:exact_text].is_a?(String)
|
49
49
|
@description << " with id #{options[:id]}" if options[:id]
|
50
|
-
@description << " with classes #{Array(options[:class]).join(',')}]" if options[:class]
|
50
|
+
@description << " with classes [#{Array(options[:class]).join(',')}]" if options[:class]
|
51
51
|
@description << selector.description(options)
|
52
52
|
@description << " that also matches the custom filter block" if @filter_block
|
53
53
|
@description
|
@@ -81,7 +81,7 @@ module Capybara
|
|
81
81
|
when :hidden then return false if node.visible?
|
82
82
|
end
|
83
83
|
|
84
|
-
res =
|
84
|
+
res = node_filters.all? do |name, filter|
|
85
85
|
if options.has_key?(name)
|
86
86
|
filter.matches?(node, options[name])
|
87
87
|
elsif filter.default?
|
@@ -91,8 +91,16 @@ module Capybara
|
|
91
91
|
end
|
92
92
|
end
|
93
93
|
|
94
|
-
res &&= node.session
|
94
|
+
res &&= if node.respond_to?(:session)
|
95
|
+
node.session.using_wait_time(0){ @filter_block.call(node) }
|
96
|
+
else
|
97
|
+
@filter_block.call(node)
|
98
|
+
end unless @filter_block.nil?
|
99
|
+
|
95
100
|
res
|
101
|
+
|
102
|
+
rescue *(node.respond_to?(:session) ? node.session.driver.invalid_element_errors : [])
|
103
|
+
return false
|
96
104
|
end
|
97
105
|
|
98
106
|
def visible
|
@@ -114,16 +122,17 @@ module Capybara
|
|
114
122
|
|
115
123
|
def xpath(exact=nil)
|
116
124
|
exact = self.exact? if exact.nil?
|
117
|
-
expr =
|
118
|
-
|
125
|
+
expr = apply_expression_filters(@expression)
|
126
|
+
expr = if expr.respond_to?(:to_xpath) and exact
|
127
|
+
expr.to_xpath(:exact)
|
119
128
|
else
|
120
|
-
|
129
|
+
expr.to_s
|
121
130
|
end
|
122
131
|
filtered_xpath(expr)
|
123
132
|
end
|
124
133
|
|
125
134
|
def css
|
126
|
-
filtered_css(@expression)
|
135
|
+
filtered_css(apply_expression_filters(@expression))
|
127
136
|
end
|
128
137
|
|
129
138
|
# @api private
|
@@ -155,16 +164,22 @@ module Capybara
|
|
155
164
|
VALID_KEYS + custom_keys
|
156
165
|
end
|
157
166
|
|
158
|
-
def
|
167
|
+
def node_filters
|
159
168
|
if options.has_key?(:filter_set)
|
160
|
-
Capybara::Selector::FilterSet.all[options[:filter_set]].
|
169
|
+
::Capybara::Selector::FilterSet.all[options[:filter_set]].node_filters
|
161
170
|
else
|
162
|
-
@selector.
|
171
|
+
@selector.node_filters
|
163
172
|
end
|
164
173
|
end
|
165
174
|
|
175
|
+
def expression_filters
|
176
|
+
filters = @selector.expression_filters
|
177
|
+
filters.merge ::Capybara::Selector::FilterSet.all[options[:filter_set]].expression_filters if options.has_key?(:filter_set)
|
178
|
+
filters
|
179
|
+
end
|
180
|
+
|
166
181
|
def custom_keys
|
167
|
-
@custom_keys ||=
|
182
|
+
@custom_keys ||= node_filters.keys + expression_filters.keys
|
168
183
|
end
|
169
184
|
|
170
185
|
def assert_valid_keys
|
@@ -200,6 +215,18 @@ module Capybara
|
|
200
215
|
expr
|
201
216
|
end
|
202
217
|
|
218
|
+
def apply_expression_filters(expr)
|
219
|
+
expression_filters.inject(expr) do |memo, (name, ef)|
|
220
|
+
if options.has_key?(name)
|
221
|
+
ef.apply_filter(memo, options[name])
|
222
|
+
elsif ef.default?
|
223
|
+
ef.apply_filter(memo, ef.default)
|
224
|
+
else
|
225
|
+
memo
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
203
230
|
def warn_exact_usage
|
204
231
|
if options.has_key?(:exact) && !supports_exact?
|
205
232
|
warn "The :exact option only has an effect on queries using the XPath#is method. Using it with the query \"#{expression.to_s}\" has no effect."
|
@@ -7,7 +7,7 @@ module Capybara
|
|
7
7
|
@type = (args.first.is_a?(Symbol) || args.first.nil?) ? args.shift : nil
|
8
8
|
# @type = (Capybara.ignore_hidden_elements or Capybara.visible_text_only) ? :visible : :all if @type.nil?
|
9
9
|
@options = if args.last.is_a?(Hash) then args.pop.dup else {} end
|
10
|
-
|
10
|
+
super(@options)
|
11
11
|
|
12
12
|
@type = (session_options.ignore_hidden_elements or session_options.visible_text_only) ? :visible : :all if @type.nil?
|
13
13
|
|
@@ -15,6 +15,7 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
|
|
15
15
|
|
16
16
|
def initialize(app, options={})
|
17
17
|
raise ArgumentError, "rack-test requires a rack application, but none was given" unless app
|
18
|
+
@session = nil
|
18
19
|
@app = app
|
19
20
|
@options = DEFAULT_OPTIONS.merge(options)
|
20
21
|
end
|
@@ -19,14 +19,18 @@ module Capybara
|
|
19
19
|
end
|
20
20
|
|
21
21
|
module DSL
|
22
|
-
|
23
|
-
|
22
|
+
class <<self
|
23
|
+
remove_method :included
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
end
|
25
|
+
def included(base)
|
26
|
+
warn "including Capybara::DSL in the global scope is not recommended!" if base == Object
|
28
27
|
|
29
|
-
|
28
|
+
if defined?(::RSpec::Matchers) && base.include?(::RSpec::Matchers)
|
29
|
+
base.send(:include, ::Capybara::RSpecMatcherProxies)
|
30
|
+
end
|
31
|
+
|
32
|
+
super
|
33
|
+
end
|
30
34
|
end
|
31
35
|
end
|
32
36
|
end
|
@@ -44,6 +44,7 @@ module Capybara
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def session_options
|
47
|
+
@context_el ||= nil
|
47
48
|
@context_el ? @context_el.session_options : Capybara.session_options
|
48
49
|
end
|
49
50
|
end
|
@@ -195,10 +196,14 @@ module Capybara
|
|
195
196
|
alias_method :failure_message_for_should_not, :failure_message_when_negated
|
196
197
|
end
|
197
198
|
|
199
|
+
# RSpec matcher for whether the element(s) matching a given selector exist
|
200
|
+
# See {Capybara::Node::Matcher#assert_selector}
|
198
201
|
def have_selector(*args, &optional_filter_block)
|
199
202
|
HaveSelector.new(*args, &optional_filter_block)
|
200
203
|
end
|
201
204
|
|
205
|
+
# RSpec matcher for whether the current element matches a given selector
|
206
|
+
# See {Capybara::Node::Matchers#assert_matches_selector}
|
202
207
|
def match_selector(*args, &optional_filter_block)
|
203
208
|
MatchSelector.new(*args, &optional_filter_block)
|
204
209
|
end
|
@@ -207,22 +212,30 @@ module Capybara
|
|
207
212
|
::RSpec::Matchers.define_negated_matcher :not_match_selector, :match_selector if defined?(::RSpec::Expectations::Version) && (Gem::Version.new(RSpec::Expectations::Version::STRING) >= Gem::Version.new('3.1'))
|
208
213
|
|
209
214
|
|
215
|
+
# RSpec matcher for whether elements(s) matching a given xpath selector exist
|
216
|
+
# See {Capybara::Node::Matchers#has_xpath?}
|
210
217
|
def have_xpath(xpath, options={}, &optional_filter_block)
|
211
218
|
HaveSelector.new(:xpath, xpath, options, &optional_filter_block)
|
212
219
|
end
|
213
220
|
|
221
|
+
# RSpec matcher for whether the current element matches a given xpath selector
|
214
222
|
def match_xpath(xpath, options={}, &optional_filter_block)
|
215
223
|
MatchSelector.new(:xpath, xpath, options, &optional_filter_block)
|
216
224
|
end
|
217
225
|
|
226
|
+
# RSpec matcher for whether elements(s) matching a given css selector exist
|
227
|
+
# See {Capybara::Node::Matchers#has_css?}
|
218
228
|
def have_css(css, options={}, &optional_filter_block)
|
219
229
|
HaveSelector.new(:css, css, options, &optional_filter_block)
|
220
230
|
end
|
221
231
|
|
232
|
+
# RSpec matcher for whether the current element matches a given css selector
|
222
233
|
def match_css(css, options={}, &optional_filter_block)
|
223
234
|
MatchSelector.new(:css, css, options, &optional_filter_block)
|
224
235
|
end
|
225
236
|
|
237
|
+
# RSpec matcher for text on the page
|
238
|
+
# See {Capybara::SessionMatchers#assert_text}
|
226
239
|
def have_text(*args)
|
227
240
|
HaveText.new(*args)
|
228
241
|
end
|
@@ -232,40 +245,56 @@ module Capybara
|
|
232
245
|
HaveTitle.new(title, options)
|
233
246
|
end
|
234
247
|
|
248
|
+
# RSpec matcher for the current path
|
249
|
+
# See {Capybara::SessionMatchers#assert_current_path}
|
235
250
|
def have_current_path(path, options = {})
|
236
251
|
HaveCurrentPath.new(path, options)
|
237
252
|
end
|
238
253
|
|
254
|
+
# RSpec matcher for links
|
255
|
+
# See {Capybara::Node::Matchers#has_link?}
|
239
256
|
def have_link(locator=nil, options={}, &optional_filter_block)
|
240
257
|
locator, options = nil, locator if locator.is_a? Hash
|
241
258
|
HaveSelector.new(:link, locator, options, &optional_filter_block)
|
242
259
|
end
|
243
260
|
|
261
|
+
# RSpec matcher for buttons
|
262
|
+
# See {Capybara::Node::Matchers#has_button?}
|
244
263
|
def have_button(locator=nil, options={}, &optional_filter_block)
|
245
264
|
locator, options = nil, locator if locator.is_a? Hash
|
246
265
|
HaveSelector.new(:button, locator, options, &optional_filter_block)
|
247
266
|
end
|
248
267
|
|
268
|
+
# RSpec matcher for links
|
269
|
+
# See {Capybara::Node::Matchers#has_field?}
|
249
270
|
def have_field(locator=nil, options={}, &optional_filter_block)
|
250
271
|
locator, options = nil, locator if locator.is_a? Hash
|
251
272
|
HaveSelector.new(:field, locator, options, &optional_filter_block)
|
252
273
|
end
|
253
274
|
|
275
|
+
# RSpec matcher for checked fields
|
276
|
+
# See {Capybara::Node::Matchers#has_checked_field?}
|
254
277
|
def have_checked_field(locator=nil, options={}, &optional_filter_block)
|
255
278
|
locator, options = nil, locator if locator.is_a? Hash
|
256
279
|
HaveSelector.new(:field, locator, options.merge(checked: true), &optional_filter_block)
|
257
280
|
end
|
258
281
|
|
282
|
+
# RSpec matcher for unchecked fields
|
283
|
+
# See {Capybara::Node::Matchers#has_unchecked_field?}
|
259
284
|
def have_unchecked_field(locator=nil, options={}, &optional_filter_block)
|
260
285
|
locator, options = nil, locator if locator.is_a? Hash
|
261
286
|
HaveSelector.new(:field, locator, options.merge(unchecked: true), &optional_filter_block)
|
262
287
|
end
|
263
288
|
|
289
|
+
# RSpec matcher for select elements
|
290
|
+
# See {Capybara::Node::Matchers#has_select?}
|
264
291
|
def have_select(locator=nil, options={}, &optional_filter_block)
|
265
292
|
locator, options = nil, locator if locator.is_a? Hash
|
266
293
|
HaveSelector.new(:select, locator, options, &optional_filter_block)
|
267
294
|
end
|
268
295
|
|
296
|
+
# RSpec matcher for table elements
|
297
|
+
# See {Capybara::Node::Matchers#has_table?}
|
269
298
|
def have_table(locator=nil, options={}, &optional_filter_block)
|
270
299
|
locator, options = nil, locator if locator.is_a? Hash
|
271
300
|
HaveSelector.new(:table, locator, options, &optional_filter_block)
|
data/lib/capybara/selector.rb
CHANGED
@@ -6,6 +6,9 @@ Capybara::Selector::FilterSet.add(:_field) do
|
|
6
6
|
filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
|
7
7
|
filter(:multiple, :boolean) { |node, value| !(value ^ node.multiple?) }
|
8
8
|
|
9
|
+
expression_filter(:name) { |xpath, val| xpath[XPath.attr(:name).equals(val)] }
|
10
|
+
expression_filter(:placeholder) { |xpath, val| xpath[XPath.attr(:placeholder).equals(val)] }
|
11
|
+
|
9
12
|
describe do |options|
|
10
13
|
desc, states = String.new, []
|
11
14
|
states << 'checked' if options[:checked] || (options[:unchecked] == false)
|
@@ -65,21 +68,21 @@ end
|
|
65
68
|
# @filter [Boolean] :disabled Match disabled field?
|
66
69
|
# @filter [Boolean] :multiple Match fields that accept multiple values
|
67
70
|
Capybara.add_selector(:field) do
|
68
|
-
xpath
|
71
|
+
xpath do |locator, options|
|
69
72
|
xpath = XPath.descendant(:input, :textarea, :select)[~XPath.attr(:type).one_of('submit', 'image', 'hidden')]
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
73
|
+
locate_field(xpath, locator, options)
|
74
|
+
end
|
75
|
+
|
76
|
+
expression_filter(:type) do |expr, type|
|
77
|
+
type = type.to_s
|
78
|
+
if ['textarea', 'select'].include?(type)
|
79
|
+
expr.axis(:self, type.to_sym)
|
80
|
+
else
|
81
|
+
expr[XPath.attr(:type).equals(type)]
|
77
82
|
end
|
78
|
-
xpath=locate_field(xpath, locator, options)
|
79
|
-
xpath
|
80
83
|
end
|
81
84
|
|
82
|
-
filter_set(:_field) # checked/unchecked/disabled/multiple
|
85
|
+
filter_set(:_field) # checked/unchecked/disabled/multiple/name/placeholder
|
83
86
|
|
84
87
|
filter(:readonly, :boolean) { |node, value| not(value ^ node.readonly?) }
|
85
88
|
filter(:with) do |node, with|
|
@@ -87,7 +90,7 @@ Capybara.add_selector(:field) do
|
|
87
90
|
end
|
88
91
|
describe do |options|
|
89
92
|
desc = String.new
|
90
|
-
(expression_filters - [:type]).each { |ef| desc << " with #{ef} #{options[ef]}" if options.has_key?(ef) }
|
93
|
+
(expression_filters.keys - [:type]).each { |ef| desc << " with #{ef} #{options[ef]}" if options.has_key?(ef) }
|
91
94
|
desc << " of type #{options[:type].inspect}" if options[:type]
|
92
95
|
desc << " with value #{options[:with].to_s.inspect}" if options.has_key?(:with)
|
93
96
|
desc
|
@@ -107,7 +110,7 @@ end
|
|
107
110
|
Capybara.add_selector(:fieldset) do
|
108
111
|
xpath(:legend) do |locator, options|
|
109
112
|
xpath = XPath.descendant(:fieldset)
|
110
|
-
xpath = xpath[XPath.attr(:id).equals(locator.to_s)
|
113
|
+
xpath = xpath[XPath.attr(:id).equals(locator.to_s).or XPath.child(:legend)[XPath.string.n.is(locator.to_s)]] unless locator.nil?
|
111
114
|
xpath = xpath[XPath.child(:legend)[XPath.string.n.is(options[:legend])]] if options[:legend]
|
112
115
|
xpath
|
113
116
|
end
|
@@ -135,11 +138,11 @@ Capybara.add_selector(:link) do
|
|
135
138
|
end
|
136
139
|
unless locator.nil?
|
137
140
|
locator = locator.to_s
|
138
|
-
matchers = XPath.attr(:id).equals(locator)
|
139
|
-
XPath.string.n.is(locator)
|
140
|
-
XPath.attr(:title).is(locator)
|
141
|
-
XPath.descendant(:img)[XPath.attr(:alt).is(locator)]
|
142
|
-
matchers
|
141
|
+
matchers = XPath.attr(:id).equals(locator).or(
|
142
|
+
XPath.string.n.is(locator)).or(
|
143
|
+
XPath.attr(:title).is(locator)).or(
|
144
|
+
XPath.descendant(:img)[XPath.attr(:alt).is(locator)])
|
145
|
+
matchers = matchers.or XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
|
143
146
|
xpath = xpath[matchers]
|
144
147
|
end
|
145
148
|
xpath = [:title].inject(xpath) { |memo, ef| memo[find_by_attr(ef, options[ef])] }
|
@@ -184,21 +187,21 @@ Capybara.add_selector(:button) do
|
|
184
187
|
|
185
188
|
unless locator.nil?
|
186
189
|
locator = locator.to_s
|
187
|
-
locator_matches = XPath.attr(:id).equals(locator)
|
188
|
-
locator_matches
|
190
|
+
locator_matches = XPath.attr(:id).equals(locator).or XPath.attr(:value).is(locator).or XPath.attr(:title).is(locator)
|
191
|
+
locator_matches = locator_matches.or XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
|
189
192
|
|
190
193
|
input_btn_xpath = input_btn_xpath[locator_matches]
|
191
194
|
|
192
|
-
btn_xpath = btn_xpath[locator_matches
|
195
|
+
btn_xpath = btn_xpath[locator_matches.or XPath.string.n.is(locator).or XPath.descendant(:img)[XPath.attr(:alt).is(locator)]]
|
193
196
|
|
194
197
|
alt_matches = XPath.attr(:alt).is(locator)
|
195
|
-
alt_matches
|
198
|
+
alt_matches = alt_matches.or XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
|
196
199
|
image_btn_xpath = image_btn_xpath[alt_matches]
|
197
200
|
end
|
198
201
|
|
199
|
-
res_xpath = input_btn_xpath
|
202
|
+
res_xpath = input_btn_xpath.union(btn_xpath).union(image_btn_xpath)
|
200
203
|
|
201
|
-
res_xpath = expression_filters.inject(res_xpath) { |memo, ef| memo[find_by_attr(ef, options[ef])] }
|
204
|
+
res_xpath = expression_filters.keys.inject(res_xpath) { |memo, ef| memo[find_by_attr(ef, options[ef])] }
|
202
205
|
|
203
206
|
res_xpath
|
204
207
|
end
|
@@ -220,7 +223,7 @@ end
|
|
220
223
|
Capybara.add_selector(:link_or_button) do
|
221
224
|
label "link or button"
|
222
225
|
xpath do |locator, options|
|
223
|
-
self.class.all.values_at(:link, :button).map {|selector| selector.xpath.call(locator, options)}.reduce(
|
226
|
+
self.class.all.values_at(:link, :button).map {|selector| selector.xpath.call(locator, options)}.reduce(:union)
|
224
227
|
end
|
225
228
|
|
226
229
|
filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| node.tag_name == "a" or not(value ^ node.disabled?) }
|
@@ -244,20 +247,21 @@ end
|
|
244
247
|
#
|
245
248
|
Capybara.add_selector(:fillable_field) do
|
246
249
|
label "field"
|
247
|
-
xpath
|
250
|
+
xpath do |locator, options|
|
248
251
|
xpath = XPath.descendant(:input, :textarea)[~XPath.attr(:type).one_of('submit', 'image', 'radio', 'checkbox', 'hidden', 'file')]
|
249
|
-
if options[:type]
|
250
|
-
type=options[:type].to_s
|
251
|
-
if ['textarea'].include?(type)
|
252
|
-
xpath = XPath.descendant(type.to_sym)
|
253
|
-
else
|
254
|
-
xpath = xpath[XPath.attr(:type).equals(type)]
|
255
|
-
end
|
256
|
-
end
|
257
252
|
locate_field(xpath, locator, options)
|
258
253
|
end
|
259
254
|
|
260
|
-
|
255
|
+
expression_filter(:type) do |expr, type|
|
256
|
+
type = type.to_s
|
257
|
+
if ['textarea'].include?(type)
|
258
|
+
expr.axis(:self, type.to_sym)
|
259
|
+
else
|
260
|
+
expr[XPath.attr(:type).equals(type)]
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
filter_set(:_field, [:disabled, :multiple, :name, :placeholder])
|
261
265
|
|
262
266
|
filter(:with) do |node, with|
|
263
267
|
with.is_a?(Regexp) ? node.value =~ with : node.value == with.to_s
|
@@ -286,12 +290,12 @@ end
|
|
286
290
|
#
|
287
291
|
Capybara.add_selector(:radio_button) do
|
288
292
|
label "radio button"
|
289
|
-
xpath
|
293
|
+
xpath do |locator, options|
|
290
294
|
xpath = XPath.descendant(:input)[XPath.attr(:type).equals('radio')]
|
291
295
|
locate_field(xpath, locator, options)
|
292
296
|
end
|
293
297
|
|
294
|
-
filter_set(:_field, [:checked, :unchecked, :disabled])
|
298
|
+
filter_set(:_field, [:checked, :unchecked, :disabled, :name])
|
295
299
|
|
296
300
|
filter(:option) { |node, value| node.value == value.to_s }
|
297
301
|
|
@@ -317,12 +321,12 @@ end
|
|
317
321
|
# @filter [String] :option Match the value
|
318
322
|
#
|
319
323
|
Capybara.add_selector(:checkbox) do
|
320
|
-
xpath
|
324
|
+
xpath do |locator, options|
|
321
325
|
xpath = XPath.descendant(:input)[XPath.attr(:type).equals('checkbox')]
|
322
326
|
locate_field(xpath, locator, options)
|
323
327
|
end
|
324
328
|
|
325
|
-
filter_set(:_field, [:checked, :unchecked, :disabled])
|
329
|
+
filter_set(:_field, [:checked, :unchecked, :disabled, :name])
|
326
330
|
|
327
331
|
filter(:option) { |node, value| node.value == value.to_s }
|
328
332
|
|
@@ -348,15 +352,16 @@ end
|
|
348
352
|
# @filter [Array<String>] :options Exact match options
|
349
353
|
# @filter [Array<String>] :with_options Partial match options
|
350
354
|
# @filter [String, Array<String>] :selected Match the selection(s)
|
355
|
+
# @filter [String, Array<String>] :with_selected Partial match the selection(s)
|
351
356
|
#
|
352
357
|
Capybara.add_selector(:select) do
|
353
358
|
label "select box"
|
354
|
-
xpath
|
359
|
+
xpath do |locator, options|
|
355
360
|
xpath = XPath.descendant(:select)
|
356
361
|
locate_field(xpath, locator, options)
|
357
362
|
end
|
358
363
|
|
359
|
-
filter_set(:_field, [:disabled, :multiple])
|
364
|
+
filter_set(:_field, [:disabled, :multiple, :name, :placeholder])
|
360
365
|
|
361
366
|
filter(:options) do |node, options|
|
362
367
|
if node.visible?
|
@@ -376,8 +381,13 @@ Capybara.add_selector(:select) do
|
|
376
381
|
end
|
377
382
|
|
378
383
|
filter(:selected) do |node, selected|
|
379
|
-
actual = node.all(:xpath, './/option', visible: false).select
|
380
|
-
|
384
|
+
actual = node.all(:xpath, './/option', visible: false).select(&:selected?).map { |option| option.text(:all) }
|
385
|
+
Array(selected).sort == actual.sort
|
386
|
+
end
|
387
|
+
|
388
|
+
filter(:with_selected) do |node, selected|
|
389
|
+
actual = node.all(:xpath, './/option', visible: false).select(&:selected?).map { |option| option.text(:all) }
|
390
|
+
(Array(selected) - actual).empty?
|
381
391
|
end
|
382
392
|
|
383
393
|
describe do |options|
|
@@ -385,6 +395,7 @@ Capybara.add_selector(:select) do
|
|
385
395
|
desc << " with options #{options[:options].inspect}" if options[:options]
|
386
396
|
desc << " with at least options #{options[:with_options].inspect}" if options[:with_options]
|
387
397
|
desc << " with #{options[:selected].inspect} selected" if options[:selected]
|
398
|
+
desc << " with at least #{options[:with_selected].inspect} selected" if options[:with_selected]
|
388
399
|
desc << describe_all_expression_filters(options)
|
389
400
|
desc
|
390
401
|
end
|
@@ -429,12 +440,12 @@ end
|
|
429
440
|
#
|
430
441
|
Capybara.add_selector(:file_field) do
|
431
442
|
label "file field"
|
432
|
-
xpath
|
443
|
+
xpath do |locator, options|
|
433
444
|
xpath = XPath.descendant(:input)[XPath.attr(:type).equals('file')]
|
434
445
|
locate_field(xpath, locator, options)
|
435
446
|
end
|
436
447
|
|
437
|
-
filter_set(:_field, [:disabled, :multiple])
|
448
|
+
filter_set(:_field, [:disabled, :multiple, :name])
|
438
449
|
|
439
450
|
describe do |options|
|
440
451
|
desc = String.new
|
@@ -454,7 +465,7 @@ Capybara.add_selector(:label) do
|
|
454
465
|
label "label"
|
455
466
|
xpath(:for) do |locator, options|
|
456
467
|
xpath = XPath.descendant(:label)
|
457
|
-
xpath = xpath[XPath.string.n.is(locator.to_s)
|
468
|
+
xpath = xpath[XPath.string.n.is(locator.to_s).or XPath.attr(:id).equals(locator.to_s)] unless locator.nil?
|
458
469
|
if options.has_key?(:for) && !options[:for].is_a?(Capybara::Node::Element)
|
459
470
|
xpath = xpath[XPath.attr(:for).equals(options[:for].to_s).or((~XPath.attr(:for)).and(XPath.descendant()[XPath.attr(:id).equals(options[:for].to_s)]))]
|
460
471
|
end
|
@@ -493,7 +504,7 @@ end
|
|
493
504
|
Capybara.add_selector(:table) do
|
494
505
|
xpath(:caption) do |locator, options|
|
495
506
|
xpath = XPath.descendant(:table)
|
496
|
-
xpath = xpath[XPath.attr(:id).equals(locator.to_s)
|
507
|
+
xpath = xpath[XPath.attr(:id).equals(locator.to_s).or XPath.descendant(:caption).is(locator.to_s)] unless locator.nil?
|
497
508
|
xpath = xpath[XPath.descendant(:caption).equals(options[:caption])] if options[:caption]
|
498
509
|
xpath
|
499
510
|
end
|
@@ -516,9 +527,9 @@ end
|
|
516
527
|
#
|
517
528
|
Capybara.add_selector(:frame) do
|
518
529
|
xpath(:name) do |locator, options|
|
519
|
-
xpath = XPath.descendant(:iframe)
|
520
|
-
xpath = xpath[XPath.attr(:id).equals(locator.to_s)
|
521
|
-
xpath = expression_filters.inject(xpath) { |memo, ef| memo[find_by_attr(ef, options[ef])] }
|
530
|
+
xpath = XPath.descendant(:iframe).union(XPath.descendant(:frame))
|
531
|
+
xpath = xpath[XPath.attr(:id).equals(locator.to_s).or XPath.attr(:name).equals(locator)] unless locator.nil?
|
532
|
+
xpath = expression_filters.keys.inject(xpath) { |memo, ef| memo[find_by_attr(ef, options[ef])] }
|
522
533
|
xpath
|
523
534
|
end
|
524
535
|
|