watir-webdriver 0.9.1 → 0.9.2
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/.gitignore +3 -0
- data/.travis.yml +7 -3
- data/CHANGES.md +6 -0
- data/Rakefile +17 -5
- data/lib/watir-webdriver.rb +16 -6
- data/lib/watir-webdriver/after_hooks.rb +2 -2
- data/lib/watir-webdriver/browser.rb +5 -5
- data/lib/watir-webdriver/cell_container.rb +4 -10
- data/lib/watir-webdriver/element_collection.rb +28 -7
- data/lib/watir-webdriver/elements/button.rb +0 -18
- data/lib/watir-webdriver/elements/cell.rb +17 -0
- data/lib/watir-webdriver/elements/checkbox.rb +0 -3
- data/lib/watir-webdriver/elements/element.rb +37 -10
- data/lib/watir-webdriver/elements/file_field.rb +0 -3
- data/lib/watir-webdriver/elements/hidden.rb +0 -3
- data/lib/watir-webdriver/elements/html_elements.rb +0 -219
- data/lib/watir-webdriver/elements/iframe.rb +29 -19
- data/lib/watir-webdriver/elements/row.rb +17 -0
- data/lib/watir-webdriver/elements/svg_elements.rb +0 -120
- data/lib/watir-webdriver/elements/table.rb +1 -1
- data/lib/watir-webdriver/elements/table_cell.rb +0 -28
- data/lib/watir-webdriver/elements/table_row.rb +1 -30
- data/lib/watir-webdriver/elements/text_area.rb +0 -17
- data/lib/watir-webdriver/elements/text_field.rb +2 -8
- data/lib/watir-webdriver/generator/base/visitor.rb +1 -10
- data/lib/watir-webdriver/locators.rb +22 -0
- data/lib/watir-webdriver/locators/button/locator.rb +38 -0
- data/lib/watir-webdriver/locators/button/selector_builder.rb +27 -0
- data/lib/watir-webdriver/locators/button/selector_builder/xpath.rb +29 -0
- data/lib/watir-webdriver/locators/button/validator.rb +14 -0
- data/lib/watir-webdriver/locators/cell/locator.rb +17 -0
- data/lib/watir-webdriver/locators/cell/selector_builder.rb +24 -0
- data/lib/watir-webdriver/locators/element/locator.rb +249 -0
- data/lib/watir-webdriver/locators/element/selector_builder.rb +147 -0
- data/lib/watir-webdriver/locators/element/selector_builder/css.rb +65 -0
- data/lib/watir-webdriver/locators/element/selector_builder/xpath.rb +72 -0
- data/lib/watir-webdriver/locators/element/validator.rb +22 -0
- data/lib/watir-webdriver/locators/row/locator.rb +17 -0
- data/lib/watir-webdriver/locators/row/selector_builder.rb +29 -0
- data/lib/watir-webdriver/locators/text_area/locator.rb +13 -0
- data/lib/watir-webdriver/locators/text_area/selector_builder.rb +22 -0
- data/lib/watir-webdriver/locators/text_field/locator.rb +42 -0
- data/lib/watir-webdriver/locators/text_field/selector_builder.rb +34 -0
- data/lib/watir-webdriver/locators/text_field/selector_builder/xpath.rb +19 -0
- data/lib/watir-webdriver/locators/text_field/validator.rb +20 -0
- data/lib/watir-webdriver/row_container.rb +3 -9
- data/lib/watir-webdriver/version.rb +1 -1
- data/lib/watir-webdriver/wait.rb +6 -3
- data/spec/always_locate_spec.rb +2 -1
- data/spec/element_locator_spec.rb +1 -1
- data/spec/element_spec.rb +8 -40
- data/spec/implementation.rb +52 -45
- data/spec/locator_spec_helper.rb +8 -2
- data/support/travis.sh +17 -6
- metadata +25 -10
- data/lib/watir-webdriver/locators/button_locator.rb +0 -85
- data/lib/watir-webdriver/locators/child_cell_locator.rb +0 -32
- data/lib/watir-webdriver/locators/child_row_locator.rb +0 -37
- data/lib/watir-webdriver/locators/element_locator.rb +0 -470
- data/lib/watir-webdriver/locators/text_area_locator.rb +0 -24
- data/lib/watir-webdriver/locators/text_field_locator.rb +0 -93
@@ -1,85 +0,0 @@
|
|
1
|
-
module Watir
|
2
|
-
class ButtonLocator < ElementLocator
|
3
|
-
|
4
|
-
def locate_all
|
5
|
-
find_all_by_multiple
|
6
|
-
end
|
7
|
-
|
8
|
-
private
|
9
|
-
|
10
|
-
def wd_find_first_by(how, what)
|
11
|
-
if how == :tag_name
|
12
|
-
how = :xpath
|
13
|
-
what = ".//button | .//input[#{attribute_expression type: Button::VALID_TYPES}]"
|
14
|
-
end
|
15
|
-
|
16
|
-
super
|
17
|
-
end
|
18
|
-
|
19
|
-
def build_wd_selector(selectors)
|
20
|
-
return if selectors.values.any? { |e| e.kind_of? Regexp }
|
21
|
-
|
22
|
-
selectors.delete(:tag_name) || raise("internal error: no tag_name?!")
|
23
|
-
|
24
|
-
@building = :button
|
25
|
-
button_attr_exp = attribute_expression(selectors)
|
26
|
-
|
27
|
-
@building = :input
|
28
|
-
selectors[:type] = Button::VALID_TYPES
|
29
|
-
input_attr_exp = attribute_expression(selectors)
|
30
|
-
|
31
|
-
xpath = ".//button"
|
32
|
-
xpath << "[#{button_attr_exp}]" unless button_attr_exp.empty?
|
33
|
-
xpath << " | .//input"
|
34
|
-
xpath << "[#{input_attr_exp}]"
|
35
|
-
|
36
|
-
p build_wd_selector: xpath if $DEBUG
|
37
|
-
|
38
|
-
[:xpath, xpath]
|
39
|
-
end
|
40
|
-
|
41
|
-
def lhs_for(key)
|
42
|
-
if @building == :input && key == :text
|
43
|
-
"@value"
|
44
|
-
else
|
45
|
-
super
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def equal_pair(key, value)
|
50
|
-
if @building == :button && key == :value
|
51
|
-
# :value should look for both node text and @value attribute
|
52
|
-
text = XpathSupport.escape(value)
|
53
|
-
"(text()=#{text} or @value=#{text})"
|
54
|
-
else
|
55
|
-
super
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def matches_selector?(element, selector)
|
60
|
-
if selector.key?(:value)
|
61
|
-
copy = selector.dup
|
62
|
-
value = copy.delete(:value)
|
63
|
-
|
64
|
-
super(element, copy) && (value === fetch_value(element, :value) || value === fetch_value(element, :text))
|
65
|
-
else
|
66
|
-
super
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def can_convert_regexp_to_contains?
|
71
|
-
# regexp conversion won't work with the complex xpath selector
|
72
|
-
false
|
73
|
-
end
|
74
|
-
|
75
|
-
def tag_name_matches?(tag_name, _)
|
76
|
-
!!(/^(input|button)$/ === tag_name)
|
77
|
-
end
|
78
|
-
|
79
|
-
def validate_element(element)
|
80
|
-
return if element.tag_name.downcase == "input" && !Button::VALID_TYPES.include?(element.attribute(:type))
|
81
|
-
super
|
82
|
-
end
|
83
|
-
|
84
|
-
end # ButtonLocator
|
85
|
-
end # Watir
|
@@ -1,32 +0,0 @@
|
|
1
|
-
module Watir
|
2
|
-
class ChildCellLocator < ElementLocator
|
3
|
-
|
4
|
-
def locate_all
|
5
|
-
find_all_by_multiple
|
6
|
-
end
|
7
|
-
|
8
|
-
private
|
9
|
-
|
10
|
-
def by_id
|
11
|
-
nil
|
12
|
-
end
|
13
|
-
|
14
|
-
def build_wd_selector(selectors)
|
15
|
-
return if selectors.values.any? { |e| e.kind_of? Regexp }
|
16
|
-
|
17
|
-
expressions = %w[./th ./td]
|
18
|
-
attr_expr = attribute_expression(selectors)
|
19
|
-
|
20
|
-
unless attr_expr.empty?
|
21
|
-
expressions.map! { |e| "#{e}[#{attr_expr}]" }
|
22
|
-
end
|
23
|
-
|
24
|
-
xpath = expressions.join(" | ")
|
25
|
-
|
26
|
-
p build_wd_selector: xpath if $DEBUG
|
27
|
-
|
28
|
-
[:xpath, xpath]
|
29
|
-
end
|
30
|
-
|
31
|
-
end # ChildCellLocator
|
32
|
-
end # Watir
|
@@ -1,37 +0,0 @@
|
|
1
|
-
module Watir
|
2
|
-
class ChildRowLocator < ElementLocator
|
3
|
-
|
4
|
-
def locate_all
|
5
|
-
find_all_by_multiple
|
6
|
-
end
|
7
|
-
|
8
|
-
private
|
9
|
-
|
10
|
-
def by_id
|
11
|
-
nil # avoid this
|
12
|
-
end
|
13
|
-
|
14
|
-
def build_wd_selector(selectors)
|
15
|
-
return if selectors.values.any? { |e| e.kind_of? Regexp }
|
16
|
-
selectors.delete(:tag_name) || raise("internal error: no tag_name?!")
|
17
|
-
|
18
|
-
expressions = %w[./tr]
|
19
|
-
unless %w[tbody tfoot thead].include?(@wd.tag_name.downcase)
|
20
|
-
expressions += %w[./tbody/tr ./thead/tr ./tfoot/tr]
|
21
|
-
end
|
22
|
-
|
23
|
-
attr_expr = attribute_expression(selectors)
|
24
|
-
|
25
|
-
unless attr_expr.empty?
|
26
|
-
expressions.map! { |e| "#{e}[#{attr_expr}]" }
|
27
|
-
end
|
28
|
-
|
29
|
-
xpath = expressions.join(" | ")
|
30
|
-
|
31
|
-
p build_wd_selector: xpath if $DEBUG
|
32
|
-
|
33
|
-
[:xpath, xpath]
|
34
|
-
end
|
35
|
-
|
36
|
-
end # ChildRowLocator
|
37
|
-
end # Watir
|
@@ -1,470 +0,0 @@
|
|
1
|
-
module Watir
|
2
|
-
class ElementLocator
|
3
|
-
include Watir::Exception
|
4
|
-
|
5
|
-
WD_FINDERS = [
|
6
|
-
:class,
|
7
|
-
:class_name,
|
8
|
-
:css,
|
9
|
-
:id,
|
10
|
-
:link,
|
11
|
-
:link_text,
|
12
|
-
:name,
|
13
|
-
:partial_link_text,
|
14
|
-
:tag_name,
|
15
|
-
:xpath
|
16
|
-
]
|
17
|
-
|
18
|
-
WILDCARD_ATTRIBUTE = /^(aria|data)_(.+)$/
|
19
|
-
|
20
|
-
# Regular expressions that can be reliably converted to xpath `contains`
|
21
|
-
# expressions in order to optimize the locator.
|
22
|
-
#
|
23
|
-
CONVERTABLE_REGEXP = %r{
|
24
|
-
\A
|
25
|
-
([^\[\]\\^$.|?*+()]*) # leading literal characters
|
26
|
-
[^|]*? # do not try to convert expressions with alternates
|
27
|
-
([^\[\]\\^$.|?*+()]*) # trailing literal characters
|
28
|
-
\z
|
29
|
-
}x
|
30
|
-
|
31
|
-
def initialize(wd, selector, valid_attributes)
|
32
|
-
@wd = wd
|
33
|
-
@selector = selector.dup
|
34
|
-
@valid_attributes = valid_attributes
|
35
|
-
end
|
36
|
-
|
37
|
-
def locate
|
38
|
-
e = by_id and return e # short-circuit if :id is given
|
39
|
-
|
40
|
-
if @selector.size == 1
|
41
|
-
element = find_first_by_one
|
42
|
-
else
|
43
|
-
element = find_first_by_multiple
|
44
|
-
end
|
45
|
-
|
46
|
-
# This actually only applies when finding by xpath/css - browser.text_field(:xpath, "//input[@type='radio']")
|
47
|
-
# We don't need to validate the element if we built the xpath ourselves.
|
48
|
-
# It is also used to alter behavior of methods locating more than one type of element
|
49
|
-
# (e.g. text_field locates both input and textarea)
|
50
|
-
validate_element(element) if element
|
51
|
-
rescue Selenium::WebDriver::Error::NoSuchElementError, Selenium::WebDriver::Error::StaleElementReferenceError
|
52
|
-
nil
|
53
|
-
end
|
54
|
-
|
55
|
-
def locate_all
|
56
|
-
if @selector.size == 1
|
57
|
-
find_all_by_one
|
58
|
-
else
|
59
|
-
find_all_by_multiple
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
private
|
64
|
-
|
65
|
-
def find_first_by_one
|
66
|
-
how, what = @selector.to_a.first
|
67
|
-
check_type how, what
|
68
|
-
|
69
|
-
if WD_FINDERS.include?(how)
|
70
|
-
wd_find_first_by(how, what)
|
71
|
-
else
|
72
|
-
find_first_by_multiple
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def find_all_by_one
|
77
|
-
how, what = @selector.to_a.first
|
78
|
-
check_type how, what
|
79
|
-
|
80
|
-
if WD_FINDERS.include?(how)
|
81
|
-
wd_find_all_by(how, what)
|
82
|
-
else
|
83
|
-
find_all_by_multiple
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def find_first_by_multiple
|
88
|
-
selector = normalized_selector
|
89
|
-
|
90
|
-
idx = selector.delete(:index)
|
91
|
-
how, what = given_xpath_or_css(selector) || build_wd_selector(selector)
|
92
|
-
|
93
|
-
if how
|
94
|
-
# could build xpath/css for selector
|
95
|
-
if idx
|
96
|
-
@wd.find_elements(how, what)[idx]
|
97
|
-
else
|
98
|
-
@wd.find_element(how, what)
|
99
|
-
end
|
100
|
-
else
|
101
|
-
# can't use xpath, probably a regexp in there
|
102
|
-
if idx
|
103
|
-
wd_find_by_regexp_selector(selector, :select)[idx]
|
104
|
-
else
|
105
|
-
wd_find_by_regexp_selector(selector, :find)
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
def find_all_by_multiple
|
111
|
-
selector = normalized_selector
|
112
|
-
|
113
|
-
if selector.key? :index
|
114
|
-
raise ArgumentError, "can't locate all elements by :index"
|
115
|
-
end
|
116
|
-
|
117
|
-
how, what = given_xpath_or_css(selector) || build_wd_selector(selector)
|
118
|
-
if how
|
119
|
-
@wd.find_elements(how, what)
|
120
|
-
else
|
121
|
-
wd_find_by_regexp_selector(selector, :select)
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
def wd_find_first_by(how, what)
|
126
|
-
if what.kind_of? String
|
127
|
-
@wd.find_element(how, what)
|
128
|
-
else
|
129
|
-
all_elements.find { |element| fetch_value(element, how) =~ what }
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
def wd_find_all_by(how, what)
|
134
|
-
if what.kind_of? String
|
135
|
-
@wd.find_elements(how, what)
|
136
|
-
else
|
137
|
-
all_elements.select { |element| fetch_value(element, how) =~ what }
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
def wd_find_by_regexp_selector(selector, method = :find)
|
142
|
-
parent = @wd
|
143
|
-
rx_selector = delete_regexps_from(selector)
|
144
|
-
|
145
|
-
if rx_selector.key?(:label) && should_use_label_element?
|
146
|
-
label = label_from_text(rx_selector.delete(:label)) || return
|
147
|
-
if (id = label.attribute(:for))
|
148
|
-
selector[:id] = id
|
149
|
-
else
|
150
|
-
parent = label
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
how, what = build_wd_selector(selector)
|
155
|
-
|
156
|
-
unless how
|
157
|
-
raise Error, "internal error: unable to build WebDriver selector from #{selector.inspect}"
|
158
|
-
end
|
159
|
-
|
160
|
-
if how == :xpath && can_convert_regexp_to_contains?
|
161
|
-
rx_selector.each do |key, value|
|
162
|
-
next if key == :tag_name || key == :text
|
163
|
-
|
164
|
-
predicates = regexp_selector_to_predicates(key, value)
|
165
|
-
what = "(#{what})[#{predicates.join(' and ')}]" unless predicates.empty?
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
elements = parent.find_elements(how, what)
|
170
|
-
elements.__send__(method) { |el| matches_selector?(el, rx_selector) }
|
171
|
-
end
|
172
|
-
|
173
|
-
VALID_WHATS = [String, Regexp]
|
174
|
-
|
175
|
-
def check_type(how, what)
|
176
|
-
case how
|
177
|
-
when :index
|
178
|
-
unless what.kind_of?(Fixnum)
|
179
|
-
raise TypeError, "expected Fixnum, got #{what.inspect}:#{what.class}"
|
180
|
-
end
|
181
|
-
else
|
182
|
-
unless VALID_WHATS.any? { |t| what.kind_of? t }
|
183
|
-
raise TypeError, "expected one of #{VALID_WHATS.inspect}, got #{what.inspect}:#{what.class}"
|
184
|
-
end
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
|
-
def label_from_text(label_exp)
|
189
|
-
# TODO: this won't work correctly if @wd is a sub-element
|
190
|
-
@wd.find_elements(:tag_name, 'label').find do |el|
|
191
|
-
matches_selector?(el, text: label_exp)
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
def fetch_value(element, how)
|
196
|
-
case how
|
197
|
-
when :text
|
198
|
-
element.text
|
199
|
-
when :tag_name
|
200
|
-
element.tag_name.downcase
|
201
|
-
when :href
|
202
|
-
(href = element.attribute(:href)) && href.strip
|
203
|
-
else
|
204
|
-
element.attribute(how.to_s.gsub("_", "-").to_sym)
|
205
|
-
end
|
206
|
-
end
|
207
|
-
|
208
|
-
def matches_selector?(element, selector)
|
209
|
-
selector.all? do |how, what|
|
210
|
-
what === fetch_value(element, how)
|
211
|
-
end
|
212
|
-
end
|
213
|
-
|
214
|
-
def normalized_selector
|
215
|
-
selector = {}
|
216
|
-
|
217
|
-
@selector.each do |how, what|
|
218
|
-
check_type(how, what)
|
219
|
-
|
220
|
-
how, what = normalize_selector(how, what)
|
221
|
-
selector[how] = what
|
222
|
-
end
|
223
|
-
|
224
|
-
selector
|
225
|
-
end
|
226
|
-
|
227
|
-
def normalize_selector(how, what)
|
228
|
-
case how
|
229
|
-
when :tag_name, :text, :xpath, :index, :class, :label, :css
|
230
|
-
# include :class since the valid attribute is 'class_name'
|
231
|
-
# include :for since the valid attribute is 'html_for'
|
232
|
-
[how, what]
|
233
|
-
when :class_name
|
234
|
-
[:class, what]
|
235
|
-
when :caption
|
236
|
-
[:text, what]
|
237
|
-
else
|
238
|
-
assert_valid_as_attribute how
|
239
|
-
[how, what]
|
240
|
-
end
|
241
|
-
end
|
242
|
-
|
243
|
-
def delete_regexps_from(selector)
|
244
|
-
rx_selector = {}
|
245
|
-
|
246
|
-
selector.dup.each do |how, what|
|
247
|
-
next unless what.kind_of?(Regexp)
|
248
|
-
rx_selector[how] = what
|
249
|
-
selector.delete how
|
250
|
-
end
|
251
|
-
|
252
|
-
rx_selector
|
253
|
-
end
|
254
|
-
|
255
|
-
def regexp_selector_to_predicates(key, re)
|
256
|
-
return [] if re.casefold?
|
257
|
-
|
258
|
-
match = re.source.match(CONVERTABLE_REGEXP)
|
259
|
-
return [] unless match
|
260
|
-
|
261
|
-
lhs = lhs_for(key)
|
262
|
-
match.captures.reject(&:empty?).map do |literals|
|
263
|
-
"contains(#{lhs}, #{XpathSupport.escape(literals)})"
|
264
|
-
end
|
265
|
-
end
|
266
|
-
|
267
|
-
def can_convert_regexp_to_contains?
|
268
|
-
true
|
269
|
-
end
|
270
|
-
|
271
|
-
def assert_valid_as_attribute(attribute)
|
272
|
-
unless valid_attribute? attribute or attribute.to_s =~ WILDCARD_ATTRIBUTE
|
273
|
-
raise MissingWayOfFindingObjectException, "invalid attribute: #{attribute.inspect}"
|
274
|
-
end
|
275
|
-
end
|
276
|
-
|
277
|
-
def by_id
|
278
|
-
return unless id = @selector[:id] and id.kind_of? String
|
279
|
-
|
280
|
-
selector = @selector.dup
|
281
|
-
selector.delete(:id)
|
282
|
-
|
283
|
-
tag_name = selector.delete(:tag_name)
|
284
|
-
return unless selector.empty? # multiple attributes
|
285
|
-
|
286
|
-
element = @wd.find_element(:id, id)
|
287
|
-
return if tag_name && !tag_name_matches?(element.tag_name.downcase, tag_name)
|
288
|
-
|
289
|
-
element
|
290
|
-
end
|
291
|
-
|
292
|
-
def all_elements
|
293
|
-
@wd.find_elements(xpath: ".//*")
|
294
|
-
end
|
295
|
-
|
296
|
-
def tag_name_matches?(element_tag_name, tag_name)
|
297
|
-
tag_name === element_tag_name
|
298
|
-
end
|
299
|
-
|
300
|
-
def valid_attribute?(attribute)
|
301
|
-
@valid_attributes && @valid_attributes.include?(attribute)
|
302
|
-
end
|
303
|
-
|
304
|
-
def should_use_label_element?
|
305
|
-
!valid_attribute?(:label)
|
306
|
-
end
|
307
|
-
|
308
|
-
def build_wd_selector(selectors)
|
309
|
-
unless selectors.values.any? { |e| e.kind_of? Regexp }
|
310
|
-
build_css(selectors) || build_xpath(selectors)
|
311
|
-
end
|
312
|
-
end
|
313
|
-
|
314
|
-
def build_xpath(selectors)
|
315
|
-
xpath = ".//"
|
316
|
-
xpath << (selectors.delete(:tag_name) || '*').to_s
|
317
|
-
|
318
|
-
idx = selectors.delete :index
|
319
|
-
|
320
|
-
# the remaining entries should be attributes
|
321
|
-
unless selectors.empty?
|
322
|
-
xpath << "[" << attribute_expression(selectors) << "]"
|
323
|
-
end
|
324
|
-
|
325
|
-
p xpath: xpath, selectors: selectors if $DEBUG
|
326
|
-
|
327
|
-
[:xpath, xpath]
|
328
|
-
end
|
329
|
-
|
330
|
-
def build_css(selectors)
|
331
|
-
return unless use_css?(selectors)
|
332
|
-
|
333
|
-
if selectors.empty?
|
334
|
-
css = '*'
|
335
|
-
else
|
336
|
-
css = ''
|
337
|
-
css << (selectors.delete(:tag_name) || '')
|
338
|
-
|
339
|
-
klass = selectors.delete(:class)
|
340
|
-
if klass
|
341
|
-
if klass.include? ' '
|
342
|
-
css << %([class="#{css_escape klass}"])
|
343
|
-
else
|
344
|
-
css << ".#{klass}"
|
345
|
-
end
|
346
|
-
end
|
347
|
-
|
348
|
-
href = selectors.delete(:href)
|
349
|
-
if href
|
350
|
-
css << %([href~="#{css_escape href}"])
|
351
|
-
end
|
352
|
-
|
353
|
-
selectors.each do |key, value|
|
354
|
-
key = key.to_s.gsub("_", "-")
|
355
|
-
css << %([#{key}="#{css_escape value}"]) # TODO: proper escaping
|
356
|
-
end
|
357
|
-
end
|
358
|
-
|
359
|
-
[:css, css]
|
360
|
-
end
|
361
|
-
|
362
|
-
def use_css?(selectors)
|
363
|
-
return false unless Watir.prefer_css?
|
364
|
-
|
365
|
-
if selectors.key?(:text) || selectors.key?(:label) || selectors.key?(:index)
|
366
|
-
return false
|
367
|
-
end
|
368
|
-
|
369
|
-
if selectors[:tag_name] == 'input' && selectors.key?(:type)
|
370
|
-
return false
|
371
|
-
end
|
372
|
-
|
373
|
-
if selectors.key?(:class) && selectors[:class] !~ /^[\w-]+$/ui
|
374
|
-
return false
|
375
|
-
end
|
376
|
-
|
377
|
-
true
|
378
|
-
end
|
379
|
-
|
380
|
-
def attribute_expression(selectors)
|
381
|
-
selectors.map do |key, val|
|
382
|
-
if val.kind_of?(Array)
|
383
|
-
"(" + val.map { |v| equal_pair(key, v) }.join(" or ") + ")"
|
384
|
-
else
|
385
|
-
equal_pair(key, val)
|
386
|
-
end
|
387
|
-
end.join(" and ")
|
388
|
-
end
|
389
|
-
|
390
|
-
def css_escape(str)
|
391
|
-
str.gsub('"', '\\"')
|
392
|
-
end
|
393
|
-
|
394
|
-
def equal_pair(key, value)
|
395
|
-
if key == :class
|
396
|
-
klass = XpathSupport.escape " #{value} "
|
397
|
-
"contains(concat(' ', @class, ' '), #{klass})"
|
398
|
-
elsif key == :label && should_use_label_element?
|
399
|
-
# we assume :label means a corresponding label element, not the attribute
|
400
|
-
text = "normalize-space()=#{XpathSupport.escape value}"
|
401
|
-
"(@id=//label[#{text}]/@for or parent::label[#{text}])"
|
402
|
-
else
|
403
|
-
"#{lhs_for(key)}=#{XpathSupport.escape value}"
|
404
|
-
end
|
405
|
-
end
|
406
|
-
|
407
|
-
def lhs_for(key)
|
408
|
-
case key
|
409
|
-
when :text, 'text'
|
410
|
-
'normalize-space()'
|
411
|
-
when :href
|
412
|
-
# TODO: change this behaviour?
|
413
|
-
'normalize-space(@href)'
|
414
|
-
when :type
|
415
|
-
# type attributes can be upper case - downcase them
|
416
|
-
# https://github.com/watir/watir-webdriver/issues/72
|
417
|
-
XpathSupport.downcase('@type')
|
418
|
-
else
|
419
|
-
"@#{key.to_s.gsub("_", "-")}"
|
420
|
-
end
|
421
|
-
end
|
422
|
-
|
423
|
-
def validate_element(element)
|
424
|
-
tn = @selector[:tag_name]
|
425
|
-
element_tag_name = element.tag_name.downcase
|
426
|
-
|
427
|
-
return if tn && !tag_name_matches?(element_tag_name, tn)
|
428
|
-
|
429
|
-
if element_tag_name == 'input'
|
430
|
-
return if @selector[:type] && @selector[:type] != element.attribute(:type)
|
431
|
-
end
|
432
|
-
|
433
|
-
element
|
434
|
-
end
|
435
|
-
|
436
|
-
def given_xpath_or_css(selector)
|
437
|
-
xpath = selector.delete(:xpath)
|
438
|
-
css = selector.delete(:css)
|
439
|
-
return unless xpath || css
|
440
|
-
|
441
|
-
if xpath && css
|
442
|
-
raise ArgumentError, ":xpath and :css cannot be combined (#{selector.inspect})"
|
443
|
-
end
|
444
|
-
|
445
|
-
how, what = if xpath
|
446
|
-
[:xpath, xpath]
|
447
|
-
elsif css
|
448
|
-
[:css, css]
|
449
|
-
end
|
450
|
-
|
451
|
-
if selector.any? && !can_be_combined_with_xpath_or_css?(selector)
|
452
|
-
raise ArgumentError, "#{how} cannot be combined with other selectors (#{selector.inspect})"
|
453
|
-
end
|
454
|
-
|
455
|
-
[how, what]
|
456
|
-
end
|
457
|
-
|
458
|
-
def can_be_combined_with_xpath_or_css?(selector)
|
459
|
-
keys = selector.keys
|
460
|
-
return true if keys == [:tag_name]
|
461
|
-
|
462
|
-
if selector[:tag_name] == "input"
|
463
|
-
return keys == [:tag_name, :type] || keys == [:type, :tag_name]
|
464
|
-
end
|
465
|
-
|
466
|
-
false
|
467
|
-
end
|
468
|
-
|
469
|
-
end # ElementLocator
|
470
|
-
end # Watir
|