ayanko-watir-webdriver 0.1.1.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.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/.gitmodules +3 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.rdoc +55 -0
- data/Rakefile +139 -0
- data/VERSION +1 -0
- data/lib/watir-webdriver.rb +71 -0
- data/lib/watir-webdriver/attribute_helper.rb +128 -0
- data/lib/watir-webdriver/browser.rb +164 -0
- data/lib/watir-webdriver/browserbot.js +49 -0
- data/lib/watir-webdriver/cell_container.rb +19 -0
- data/lib/watir-webdriver/container.rb +40 -0
- data/lib/watir-webdriver/core_ext/string.rb +22 -0
- data/lib/watir-webdriver/element_collection.rb +96 -0
- data/lib/watir-webdriver/elements/button.rb +75 -0
- data/lib/watir-webdriver/elements/checkbox.rb +73 -0
- data/lib/watir-webdriver/elements/element.rb +265 -0
- data/lib/watir-webdriver/elements/file_field.rb +69 -0
- data/lib/watir-webdriver/elements/font.rb +11 -0
- data/lib/watir-webdriver/elements/form.rb +17 -0
- data/lib/watir-webdriver/elements/frame.rb +110 -0
- data/lib/watir-webdriver/elements/generated.rb +2541 -0
- data/lib/watir-webdriver/elements/hidden.rb +24 -0
- data/lib/watir-webdriver/elements/image.rb +51 -0
- data/lib/watir-webdriver/elements/input.rb +42 -0
- data/lib/watir-webdriver/elements/link.rb +7 -0
- data/lib/watir-webdriver/elements/option.rb +55 -0
- data/lib/watir-webdriver/elements/radio.rb +49 -0
- data/lib/watir-webdriver/elements/select.rb +216 -0
- data/lib/watir-webdriver/elements/table.rb +37 -0
- data/lib/watir-webdriver/elements/table_cell.rb +36 -0
- data/lib/watir-webdriver/elements/table_row.rb +45 -0
- data/lib/watir-webdriver/elements/table_section.rb +9 -0
- data/lib/watir-webdriver/elements/text_field.rb +97 -0
- data/lib/watir-webdriver/exception.rb +21 -0
- data/lib/watir-webdriver/extensions/alerts.rb +69 -0
- data/lib/watir-webdriver/extensions/cookies.rb +39 -0
- data/lib/watir-webdriver/extensions/firefox/webdriver.xpi +0 -0
- data/lib/watir-webdriver/extensions/nokogiri.rb +14 -0
- data/lib/watir-webdriver/extensions/performance.rb +54 -0
- data/lib/watir-webdriver/extensions/wait.rb +141 -0
- data/lib/watir-webdriver/html.rb +19 -0
- data/lib/watir-webdriver/html/generator.rb +112 -0
- data/lib/watir-webdriver/html/idl_sorter.rb +49 -0
- data/lib/watir-webdriver/html/spec_extractor.rb +111 -0
- data/lib/watir-webdriver/html/util.rb +22 -0
- data/lib/watir-webdriver/html/visitor.rb +174 -0
- data/lib/watir-webdriver/locators/button_locator.rb +74 -0
- data/lib/watir-webdriver/locators/child_cell_locator.rb +32 -0
- data/lib/watir-webdriver/locators/child_row_locator.rb +37 -0
- data/lib/watir-webdriver/locators/element_locator.rb +352 -0
- data/lib/watir-webdriver/locators/text_field_locator.rb +65 -0
- data/lib/watir-webdriver/row_container.rb +34 -0
- data/lib/watir-webdriver/window_switching.rb +105 -0
- data/lib/watir-webdriver/xpath_support.rb +28 -0
- data/lib/yard/handlers/watir.rb +57 -0
- data/spec/alert_spec.rb +49 -0
- data/spec/browser_spec.rb +42 -0
- data/spec/container_spec.rb +42 -0
- data/spec/element_locator_spec.rb +304 -0
- data/spec/element_spec.rb +13 -0
- data/spec/html/alerts.html +11 -0
- data/spec/html/keylogger.html +15 -0
- data/spec/html/wait.html +27 -0
- data/spec/implementation.rb +17 -0
- data/spec/input_spec.rb +39 -0
- data/spec/locator_spec_helper.rb +51 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/wait_spec.rb +98 -0
- data/support/html5.html +90243 -0
- data/watir-webdriver.gemspec +59 -0
- metadata +238 -0
@@ -0,0 +1,74 @@
|
|
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_xpath(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_xpath => xpath if $DEBUG
|
37
|
+
|
38
|
+
xpath
|
39
|
+
end
|
40
|
+
|
41
|
+
def lhs_for(key)
|
42
|
+
if @building == :input && key == :text
|
43
|
+
"@value"
|
44
|
+
elsif @building == :button && key == :value
|
45
|
+
"text()"
|
46
|
+
else
|
47
|
+
super
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def matches_selector?(element, rx_selector)
|
52
|
+
rx_selector = rx_selector.dup
|
53
|
+
|
54
|
+
[:value, :caption].each do |key|
|
55
|
+
if rx_selector.has_key?(key)
|
56
|
+
correct_key = element.tag_name == 'button' ? :text : :value
|
57
|
+
rx_selector[correct_key] = rx_selector.delete(key)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
super
|
62
|
+
end
|
63
|
+
|
64
|
+
def tag_name_matches?(element, _)
|
65
|
+
!!(/^(input|button)$/ === element.tag_name)
|
66
|
+
end
|
67
|
+
|
68
|
+
def validate_element(element)
|
69
|
+
return if element.tag_name == "input" && !Button::VALID_TYPES.include?(element.attribute(:type))
|
70
|
+
super
|
71
|
+
end
|
72
|
+
|
73
|
+
end # ButtonLocator
|
74
|
+
end # Watir
|
@@ -0,0 +1,32 @@
|
|
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_xpath(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_xpath => xpath if $DEBUG
|
27
|
+
|
28
|
+
xpath
|
29
|
+
end
|
30
|
+
|
31
|
+
end # ChildCellLocator
|
32
|
+
end # Watir
|
@@ -0,0 +1,37 @@
|
|
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_xpath(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)
|
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_xpath => xpath if $DEBUG
|
32
|
+
|
33
|
+
xpath
|
34
|
+
end
|
35
|
+
|
36
|
+
end # ChildRowLocator
|
37
|
+
end # Watir
|
@@ -0,0 +1,352 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Watir
|
3
|
+
class ElementLocator
|
4
|
+
include Watir::Exception
|
5
|
+
include Selenium
|
6
|
+
|
7
|
+
WD_FINDERS = [
|
8
|
+
:class,
|
9
|
+
:class_name,
|
10
|
+
:css,
|
11
|
+
:id,
|
12
|
+
:link,
|
13
|
+
:link_text,
|
14
|
+
:name,
|
15
|
+
:partial_link_text,
|
16
|
+
:tag_name,
|
17
|
+
:xpath
|
18
|
+
]
|
19
|
+
|
20
|
+
def initialize(wd, selector, valid_attributes)
|
21
|
+
@wd = wd
|
22
|
+
@selector = selector.dup
|
23
|
+
@valid_attributes = valid_attributes
|
24
|
+
end
|
25
|
+
|
26
|
+
def locate
|
27
|
+
e = by_id and return e # short-circuit if :id is given
|
28
|
+
|
29
|
+
|
30
|
+
if @selector.size == 1
|
31
|
+
element = find_first_by_one
|
32
|
+
else
|
33
|
+
element = find_first_by_multiple
|
34
|
+
end
|
35
|
+
|
36
|
+
# this actually only applies when finding by xpath - browser.text_field(:xpath, "//input[@type='radio']")
|
37
|
+
# we don't need to validate the element if we built the xpath ourselves.
|
38
|
+
validate_element(element) if element
|
39
|
+
rescue WebDriver::Error::NoSuchElementError => wde
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def locate_all
|
44
|
+
if @selector.size == 1
|
45
|
+
find_all_by_one
|
46
|
+
else
|
47
|
+
find_all_by_multiple
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def find_first_by_one
|
54
|
+
how, what = @selector.to_a.first
|
55
|
+
check_type how, what
|
56
|
+
|
57
|
+
if WD_FINDERS.include?(how)
|
58
|
+
wd_find_first_by(how, what)
|
59
|
+
else
|
60
|
+
find_first_by_multiple
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def find_all_by_one
|
65
|
+
how, what = @selector.to_a.first
|
66
|
+
check_type how, what
|
67
|
+
|
68
|
+
if WD_FINDERS.include?(how)
|
69
|
+
wd_find_all_by(how, what)
|
70
|
+
else
|
71
|
+
find_all_by_multiple
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def find_first_by_multiple
|
76
|
+
selector = normalized_selector
|
77
|
+
|
78
|
+
idx = selector.delete(:index)
|
79
|
+
xpath = given_xpath(selector) || build_xpath(selector)
|
80
|
+
|
81
|
+
if xpath
|
82
|
+
# could build xpath for selector
|
83
|
+
if idx
|
84
|
+
@wd.find_elements(:xpath, xpath)[idx]
|
85
|
+
else
|
86
|
+
@wd.find_element(:xpath, xpath)
|
87
|
+
end
|
88
|
+
else
|
89
|
+
# can't use xpath, probably a regexp in there
|
90
|
+
if idx
|
91
|
+
wd_find_by_regexp_selector(selector, :select)[idx]
|
92
|
+
else
|
93
|
+
wd_find_by_regexp_selector(selector, :find)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def find_all_by_multiple
|
99
|
+
selector = normalized_selector
|
100
|
+
|
101
|
+
if selector.has_key? :index
|
102
|
+
raise ArgumentError, "can't locate all elements by :index"
|
103
|
+
end
|
104
|
+
|
105
|
+
xpath = given_xpath(selector) || build_xpath(selector)
|
106
|
+
if xpath
|
107
|
+
@wd.find_elements(:xpath, xpath)
|
108
|
+
else
|
109
|
+
wd_find_by_regexp_selector(selector, :select)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def wd_find_first_by(how, what)
|
114
|
+
if what.kind_of? String
|
115
|
+
@wd.find_element(how, what)
|
116
|
+
else
|
117
|
+
all_elements.find { |element| fetch_value(element, how) =~ what }
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def wd_find_all_by(how, what)
|
122
|
+
if what.kind_of? String
|
123
|
+
@wd.find_elements(how, what)
|
124
|
+
else
|
125
|
+
all_elements.select { |element| fetch_value(element, how) =~ what }
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def wd_find_by_regexp_selector(selector, method = :find)
|
130
|
+
rx_selector = delete_regexps_from(selector)
|
131
|
+
|
132
|
+
if rx_selector.has_key?(:label) && should_use_label_element?
|
133
|
+
selector[:id] = id_from_label(rx_selector.delete(:label)) || return
|
134
|
+
end
|
135
|
+
|
136
|
+
xpath = build_xpath(selector) || raise("internal error: unable to build xpath from #{selector.inspect}")
|
137
|
+
|
138
|
+
elements = @wd.find_elements(:xpath, xpath)
|
139
|
+
elements.__send__(method) { |el| matches_selector?(el, rx_selector) }
|
140
|
+
end
|
141
|
+
|
142
|
+
VALID_WHATS = [String, Regexp]
|
143
|
+
|
144
|
+
def check_type(how, what)
|
145
|
+
case how
|
146
|
+
when :index
|
147
|
+
unless what.kind_of?(Fixnum)
|
148
|
+
raise TypeError, "expected Fixnum, got #{what.inspect}:#{what.class}"
|
149
|
+
end
|
150
|
+
else
|
151
|
+
unless VALID_WHATS.any? { |t| what.kind_of? t }
|
152
|
+
raise TypeError, "expected one of #{VALID_WHATS.inspect}, got #{what.inspect}:#{what.class}"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def id_from_label(label_exp)
|
158
|
+
# TODO: this won't work correctly if @wd is a sub-element
|
159
|
+
label = @wd.find_elements(:tag_name, 'label').find do |el|
|
160
|
+
matches_selector?(el, :text => label_exp)
|
161
|
+
end
|
162
|
+
|
163
|
+
label.attribute(:for) if label
|
164
|
+
end
|
165
|
+
|
166
|
+
def fetch_value(element, how)
|
167
|
+
case how
|
168
|
+
when :text
|
169
|
+
element.text
|
170
|
+
when :tag_name
|
171
|
+
element.tag_name
|
172
|
+
when :href
|
173
|
+
(href = element.attribute(:href)) && href.strip
|
174
|
+
else
|
175
|
+
element.attribute(how)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def matches_selector?(element, selector)
|
180
|
+
selector.all? do |how, what|
|
181
|
+
what === fetch_value(element, how)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def normalized_selector
|
186
|
+
selector = {}
|
187
|
+
|
188
|
+
@selector.each do |how, what|
|
189
|
+
check_type(how, what)
|
190
|
+
|
191
|
+
how, what = normalize_selector(how, what)
|
192
|
+
selector[how] = what
|
193
|
+
end
|
194
|
+
|
195
|
+
selector
|
196
|
+
end
|
197
|
+
|
198
|
+
def normalize_selector(how, what)
|
199
|
+
case how
|
200
|
+
when :tag_name, :text, :xpath, :index, :class, :for
|
201
|
+
# include :class since the valid attribute is 'class_name'
|
202
|
+
# include :for since the valid attribute is 'html_for'
|
203
|
+
[how, what]
|
204
|
+
when :class_name
|
205
|
+
[:class, what]
|
206
|
+
when :caption
|
207
|
+
[:text, what]
|
208
|
+
else
|
209
|
+
assert_valid_as_attribute how
|
210
|
+
[how, what]
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def delete_regexps_from(selector)
|
215
|
+
rx_selector = {}
|
216
|
+
|
217
|
+
selector.dup.each do |how, what|
|
218
|
+
next unless what.kind_of?(Regexp)
|
219
|
+
rx_selector[how] = what
|
220
|
+
selector.delete how
|
221
|
+
end
|
222
|
+
|
223
|
+
rx_selector
|
224
|
+
end
|
225
|
+
|
226
|
+
def assert_valid_as_attribute(attribute)
|
227
|
+
unless valid_attribute? attribute or attribute.to_s =~ /^data_.+$/
|
228
|
+
raise MissingWayOfFindingObjectException, "invalid attribute: #{attribute.inspect}"
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def by_id
|
233
|
+
return unless id = @selector[:id] and id.kind_of? String
|
234
|
+
|
235
|
+
selector = @selector.dup
|
236
|
+
selector.delete(:id)
|
237
|
+
|
238
|
+
tag_name = selector.delete(:tag_name)
|
239
|
+
return unless selector.empty? # multiple attributes
|
240
|
+
|
241
|
+
element = @wd.find_element(:id, id)
|
242
|
+
return if tag_name && !tag_name_matches?(element, tag_name)
|
243
|
+
|
244
|
+
element
|
245
|
+
rescue WebDriver::Error::NoSuchElementError => wde
|
246
|
+
nil
|
247
|
+
end
|
248
|
+
|
249
|
+
def all_elements
|
250
|
+
@wd.find_elements(:xpath => ".//*")
|
251
|
+
end
|
252
|
+
|
253
|
+
def tag_name_matches?(element, tag_name)
|
254
|
+
tag_name === element.tag_name
|
255
|
+
end
|
256
|
+
|
257
|
+
def valid_attribute?(attribute)
|
258
|
+
@valid_attributes && @valid_attributes.include?(attribute)
|
259
|
+
end
|
260
|
+
|
261
|
+
def should_use_label_element?
|
262
|
+
@selector[:tag_name] != "option"
|
263
|
+
end
|
264
|
+
|
265
|
+
def build_xpath(selectors)
|
266
|
+
return if selectors.values.any? { |e| e.kind_of? Regexp }
|
267
|
+
|
268
|
+
xpath = ".//"
|
269
|
+
xpath << (selectors.delete(:tag_name) || '*').to_s
|
270
|
+
|
271
|
+
idx = selectors.delete(:index)
|
272
|
+
|
273
|
+
# the remaining entries should be attributes
|
274
|
+
unless selectors.empty?
|
275
|
+
xpath << "[" << attribute_expression(selectors) << "]"
|
276
|
+
end
|
277
|
+
|
278
|
+
if idx
|
279
|
+
xpath << "[#{idx + 1}]"
|
280
|
+
end
|
281
|
+
|
282
|
+
p :xpath => xpath, :selectors => selectors if $DEBUG
|
283
|
+
|
284
|
+
xpath
|
285
|
+
end
|
286
|
+
|
287
|
+
def attribute_expression(selectors)
|
288
|
+
selectors.map do |key, val|
|
289
|
+
if val.kind_of?(Array)
|
290
|
+
"(" + val.map { |v| equal_pair(key, v) }.join(" or ") + ")"
|
291
|
+
else
|
292
|
+
equal_pair(key, val)
|
293
|
+
end
|
294
|
+
end.join(" and ")
|
295
|
+
end
|
296
|
+
|
297
|
+
def equal_pair(key, value)
|
298
|
+
# we assume :label means a corresponding label element, not the attribute
|
299
|
+
if key == :label && should_use_label_element?
|
300
|
+
"@id=//label[normalize-space()='#{value}']/@for"
|
301
|
+
else
|
302
|
+
"#{lhs_for(key)}='#{value}'"
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
def lhs_for(key)
|
307
|
+
case key
|
308
|
+
when :text, 'text'
|
309
|
+
'normalize-space()'
|
310
|
+
when :href
|
311
|
+
# TODO: change this behaviour?
|
312
|
+
'normalize-space(@href)'
|
313
|
+
else
|
314
|
+
"@#{key.to_s.gsub("_", "-")}"
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
def validate_element(element)
|
319
|
+
tn = @selector[:tag_name]
|
320
|
+
return if tn && !tag_name_matches?(element, tn)
|
321
|
+
|
322
|
+
if element.tag_name == 'input'
|
323
|
+
return if @selector[:type] && @selector[:type] != element.attribute(:type)
|
324
|
+
end
|
325
|
+
|
326
|
+
element
|
327
|
+
end
|
328
|
+
|
329
|
+
def given_xpath(selector)
|
330
|
+
return unless xpath = selector.delete(:xpath)
|
331
|
+
|
332
|
+
unless selector.empty? || can_be_combined_with_xpath?(selector)
|
333
|
+
raise ArgumentError, ":xpath cannot be combined with other selectors (#{selector.inspect})"
|
334
|
+
end
|
335
|
+
|
336
|
+
xpath
|
337
|
+
end
|
338
|
+
|
339
|
+
def can_be_combined_with_xpath?(selector)
|
340
|
+
# ouch - is this worth it?
|
341
|
+
keys = selector.keys
|
342
|
+
return true if keys == [:tag_name]
|
343
|
+
|
344
|
+
if selector[:tag_name] == "input"
|
345
|
+
return keys == [:tag_name, :type] || keys == [:type, :tag_name]
|
346
|
+
end
|
347
|
+
|
348
|
+
false
|
349
|
+
end
|
350
|
+
|
351
|
+
end # ElementLocator
|
352
|
+
end # Watir
|