jarib-celerity 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +42 -0
- data/License.txt +621 -0
- data/README.txt +64 -0
- data/Rakefile +12 -0
- data/lib/celerity.rb +59 -0
- data/lib/celerity/browser.rb +410 -0
- data/lib/celerity/clickable_element.rb +26 -0
- data/lib/celerity/collections.rb +148 -0
- data/lib/celerity/container.rb +488 -0
- data/lib/celerity/default_viewer.rb +10 -0
- data/lib/celerity/disabled_element.rb +27 -0
- data/lib/celerity/element.rb +241 -0
- data/lib/celerity/element_collections.rb +68 -0
- data/lib/celerity/element_locator.rb +167 -0
- data/lib/celerity/elements/button.rb +34 -0
- data/lib/celerity/elements/file_field.rb +17 -0
- data/lib/celerity/elements/form.rb +16 -0
- data/lib/celerity/elements/frame.rb +53 -0
- data/lib/celerity/elements/image.rb +57 -0
- data/lib/celerity/elements/label.rb +9 -0
- data/lib/celerity/elements/link.rb +12 -0
- data/lib/celerity/elements/meta.rb +6 -0
- data/lib/celerity/elements/non_control_elements.rb +93 -0
- data/lib/celerity/elements/option.rb +18 -0
- data/lib/celerity/elements/radio_check.rb +85 -0
- data/lib/celerity/elements/select_list.rb +81 -0
- data/lib/celerity/elements/table.rb +117 -0
- data/lib/celerity/elements/table_cell.rb +28 -0
- data/lib/celerity/elements/table_elements.rb +41 -0
- data/lib/celerity/elements/table_row.rb +36 -0
- data/lib/celerity/elements/text_field.rb +127 -0
- data/lib/celerity/exception.rb +40 -0
- data/lib/celerity/extra/method_generator.rb +158 -0
- data/lib/celerity/htmlunit.rb +41 -0
- data/lib/celerity/htmlunit/commons-codec-1.3.jar +0 -0
- data/lib/celerity/htmlunit/commons-collections-3.2.1.jar +0 -0
- data/lib/celerity/htmlunit/commons-httpclient-3.1.jar +0 -0
- data/lib/celerity/htmlunit/commons-io-1.4.jar +0 -0
- data/lib/celerity/htmlunit/commons-lang-2.4.jar +0 -0
- data/lib/celerity/htmlunit/commons-logging-1.1.1.jar +0 -0
- data/lib/celerity/htmlunit/cssparser-0.9.5.jar +0 -0
- data/lib/celerity/htmlunit/htmlunit-2.4-SNAPSHOT.jar +0 -0
- data/lib/celerity/htmlunit/htmlunit-core-js-2.4-SNAPSHOT.jar +0 -0
- data/lib/celerity/htmlunit/nekohtml-1.9.10-20081209.100757-4.jar +0 -0
- data/lib/celerity/htmlunit/sac-1.3.jar +0 -0
- data/lib/celerity/htmlunit/serializer-2.7.1.jar +0 -0
- data/lib/celerity/htmlunit/xalan-2.7.1.jar +0 -0
- data/lib/celerity/htmlunit/xercesImpl-2.8.1.jar +0 -0
- data/lib/celerity/htmlunit/xml-apis-1.3.04.jar +0 -0
- data/lib/celerity/identifier.rb +11 -0
- data/lib/celerity/input_element.rb +25 -0
- data/lib/celerity/listener.rb +106 -0
- data/lib/celerity/resources/no_viewer.png +0 -0
- data/lib/celerity/util.rb +79 -0
- data/lib/celerity/version.rb +9 -0
- data/lib/celerity/watir_compatibility.rb +85 -0
- data/tasks/benchmark.rake +4 -0
- data/tasks/deployment.rake +43 -0
- data/tasks/environment.rake +7 -0
- data/tasks/fix.rake +25 -0
- data/tasks/jar.rake +57 -0
- data/tasks/rdoc.rake +4 -0
- data/tasks/rspec.rake +30 -0
- data/tasks/simple_ci.rake +94 -0
- data/tasks/snapshot.rake +26 -0
- data/tasks/specserver.rake +21 -0
- data/tasks/website.rake +17 -0
- data/tasks/yard.rake +5 -0
- metadata +129 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
module Celerity
|
2
|
+
# Module mixed in to all elements that can have the 'disabled' attribute.
|
3
|
+
module DisabledElement
|
4
|
+
include Celerity::Exception
|
5
|
+
|
6
|
+
# Returns false if the element is disabled.
|
7
|
+
def enabled?
|
8
|
+
!disabled?
|
9
|
+
end
|
10
|
+
|
11
|
+
# Returns true if the element is disabled.
|
12
|
+
def disabled?
|
13
|
+
assert_exists unless defined?(@object) && @object
|
14
|
+
@object.isDisabled
|
15
|
+
end
|
16
|
+
alias_method :disabled, :disabled?
|
17
|
+
|
18
|
+
# Used internally.
|
19
|
+
# @api private
|
20
|
+
def assert_enabled
|
21
|
+
if disabled?
|
22
|
+
raise ObjectDisabledException, "Object #{identifier_string} is disabled"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,241 @@
|
|
1
|
+
module Celerity
|
2
|
+
|
3
|
+
# Superclass for all HTML elements.
|
4
|
+
class Element
|
5
|
+
include Exception
|
6
|
+
include Container
|
7
|
+
|
8
|
+
attr_reader :container, :object
|
9
|
+
|
10
|
+
# number of spaces that separate the property from the value in the create_string method
|
11
|
+
TO_S_SIZE = 14
|
12
|
+
|
13
|
+
# HTML 4.01 Transitional DTD
|
14
|
+
HTML_401_TRANSITIONAL = {
|
15
|
+
:core => [:class, :id, :style, :title],
|
16
|
+
:cell_halign => [:align, :char, :charoff],
|
17
|
+
:cell_valign => [:valign],
|
18
|
+
:i18n => [:dir, :lang],
|
19
|
+
:event => [:onclick, :ondblclick, :onmousedown, :onmouseup, :onmouseover,
|
20
|
+
:onmousemove, :onmouseout, :onkeypress, :onkeydown, :onkeyup],
|
21
|
+
:sloppy => [:name, :value]
|
22
|
+
}
|
23
|
+
|
24
|
+
CELLHALIGN_ATTRIBUTES = HTML_401_TRANSITIONAL[:cell_halign]
|
25
|
+
CELLVALIGN_ATTRIBUTES = HTML_401_TRANSITIONAL[:cell_valign]
|
26
|
+
BASE_ATTRIBUTES = HTML_401_TRANSITIONAL.values_at(:core, :i18n, :event, :sloppy).flatten
|
27
|
+
ATTRIBUTES = BASE_ATTRIBUTES
|
28
|
+
TAGS = []
|
29
|
+
|
30
|
+
DEFAULT_HOW = nil
|
31
|
+
|
32
|
+
# @api private
|
33
|
+
def initialize(container, *args)
|
34
|
+
self.container = container
|
35
|
+
|
36
|
+
case args.size
|
37
|
+
when 2
|
38
|
+
@conditions = { args[0] => args[1] }
|
39
|
+
when 1
|
40
|
+
if args.first.is_a? Hash
|
41
|
+
@conditions = args.first
|
42
|
+
elsif (how = self.class::DEFAULT_HOW)
|
43
|
+
@conditions = { how => args.first }
|
44
|
+
else
|
45
|
+
raise ArgumentError, "wrong number of arguments (1 for 2)"
|
46
|
+
end
|
47
|
+
else
|
48
|
+
raise ArgumentError, "wrong number of arguments (#{args.size} for 2)"
|
49
|
+
end
|
50
|
+
|
51
|
+
@conditions.freeze
|
52
|
+
end
|
53
|
+
|
54
|
+
# Get the parent element
|
55
|
+
# @return [Celerity::Element, nil] subclass of Celerity::Element, or nil if no parent was found
|
56
|
+
def parent
|
57
|
+
assert_exists
|
58
|
+
|
59
|
+
obj = @object.parentNode
|
60
|
+
until element_class = Celerity::Util.htmlunit2celerity(obj.class)
|
61
|
+
return nil if obj.nil?
|
62
|
+
obj = obj.parentNode
|
63
|
+
end
|
64
|
+
|
65
|
+
element_class.new(@container, :object, obj)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Sets the focus to this element.
|
69
|
+
def focus
|
70
|
+
assert_exists
|
71
|
+
@object.focus
|
72
|
+
end
|
73
|
+
|
74
|
+
# Used internally. Find the element on the page.
|
75
|
+
# @api private
|
76
|
+
def locate
|
77
|
+
@object = ElementLocator.new(@container, self.class).find_by_conditions(@conditions)
|
78
|
+
end
|
79
|
+
|
80
|
+
# @return [String] A string representation of the element.
|
81
|
+
def to_s
|
82
|
+
assert_exists
|
83
|
+
create_string(@object)
|
84
|
+
end
|
85
|
+
|
86
|
+
# @param [String, #to_s] The attribute.
|
87
|
+
# @return [String] The value of the given attribute.
|
88
|
+
def attribute_value(attribute)
|
89
|
+
assert_exists
|
90
|
+
@object.getAttribute(attribute.to_s)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Check if the element is visible to the user or not.
|
94
|
+
# Note that this only takes the _style attribute_ of the element (and
|
95
|
+
# its parents) into account - styles from applied CSS is not considered.
|
96
|
+
#
|
97
|
+
# @return [boolean]
|
98
|
+
def visible?
|
99
|
+
obj = self
|
100
|
+
while obj
|
101
|
+
return false if obj.respond_to?(:type) && obj.type == 'hidden'
|
102
|
+
return false if obj.style =~ /display\s*:\s*none|visibility\s*:\s*hidden/
|
103
|
+
obj = obj.parent
|
104
|
+
end
|
105
|
+
|
106
|
+
return true
|
107
|
+
end
|
108
|
+
|
109
|
+
# Used internally to ensure the element actually exists.
|
110
|
+
#
|
111
|
+
# @raise [Celerity::Exception::UnknownObjectException] if the element can't be found.
|
112
|
+
# @api private
|
113
|
+
def assert_exists
|
114
|
+
locate
|
115
|
+
unless @object
|
116
|
+
raise UnknownObjectException, "Unable to locate #{self.class.name[/::(.*)$/, 1]}, using #{identifier_string}"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Checks if the element exists.
|
121
|
+
# @return [true, false]
|
122
|
+
def exists?
|
123
|
+
assert_exists
|
124
|
+
true
|
125
|
+
rescue UnknownObjectException, UnknownFrameException
|
126
|
+
false
|
127
|
+
end
|
128
|
+
alias_method :exist?, :exists?
|
129
|
+
|
130
|
+
# Return a text representation of the element as it would appear in a browser.
|
131
|
+
#
|
132
|
+
# @see inner_text
|
133
|
+
# @return [String]
|
134
|
+
def text
|
135
|
+
assert_exists
|
136
|
+
@object.asText.strip # this must behave like ElementLocator
|
137
|
+
end
|
138
|
+
|
139
|
+
# Return the text content of this DOM node, disregarding its visibility.
|
140
|
+
#
|
141
|
+
# (Celerity-specific?)
|
142
|
+
#
|
143
|
+
# @see text
|
144
|
+
# @return [String]
|
145
|
+
def inner_text
|
146
|
+
assert_exists
|
147
|
+
Celerity::Util.normalize_text @object.getTextContent
|
148
|
+
end
|
149
|
+
|
150
|
+
# @return [String] The normative XML representation of the element (including children).
|
151
|
+
def to_xml
|
152
|
+
assert_exists
|
153
|
+
@object.asXml
|
154
|
+
end
|
155
|
+
alias_method :asXml, :to_xml
|
156
|
+
alias_method :as_xml, :to_xml
|
157
|
+
alias_method :html, :to_xml
|
158
|
+
|
159
|
+
# @return [String] A string representation of the element's attributes.
|
160
|
+
def attribute_string
|
161
|
+
assert_exists
|
162
|
+
|
163
|
+
result = ''
|
164
|
+
@object.getAttributes.each do |attribute|
|
165
|
+
result << %Q{#{attribute.getName}="#{attribute.getHtmlValue.to_s}"}
|
166
|
+
end
|
167
|
+
|
168
|
+
result
|
169
|
+
end
|
170
|
+
|
171
|
+
# return the canonical xpath for this element (Celerity-specific)
|
172
|
+
def xpath
|
173
|
+
assert_exists
|
174
|
+
@object.getCanonicalXPath
|
175
|
+
end
|
176
|
+
|
177
|
+
# Dynamically get element attributes.
|
178
|
+
#
|
179
|
+
# @see ATTRIBUTES constant for a list of valid methods for a given element.
|
180
|
+
#
|
181
|
+
# @return [String] The resulting attribute.
|
182
|
+
# @raise [NoMethodError] if the element doesn't support this attribute.
|
183
|
+
def method_missing(meth, *args, &blk)
|
184
|
+
assert_exists
|
185
|
+
|
186
|
+
meth = selector_to_attribute(meth)
|
187
|
+
|
188
|
+
if self.class::ATTRIBUTES.include?(meth)
|
189
|
+
return @object.getAttributeValue(meth.to_s)
|
190
|
+
end
|
191
|
+
|
192
|
+
Log.warn "Element\#method_missing calling super with #{meth.inspect}"
|
193
|
+
super
|
194
|
+
end
|
195
|
+
|
196
|
+
def respond_to?(meth, include_private = false)
|
197
|
+
meth = selector_to_attribute(meth)
|
198
|
+
return true if self.class::ATTRIBUTES.include?(meth)
|
199
|
+
super
|
200
|
+
end
|
201
|
+
|
202
|
+
private
|
203
|
+
|
204
|
+
def create_string(element)
|
205
|
+
ret = []
|
206
|
+
|
207
|
+
unless (tag = element.getTagName).empty?
|
208
|
+
ret << "tag:".ljust(TO_S_SIZE) + tag
|
209
|
+
end
|
210
|
+
|
211
|
+
element.getAttributes.each do |attribute|
|
212
|
+
ret << " #{attribute.getName}:".ljust(TO_S_SIZE+2) + attribute.getHtmlValue.to_s
|
213
|
+
end
|
214
|
+
|
215
|
+
unless (text = element.asText).empty?
|
216
|
+
ret << " text:".ljust(TO_S_SIZE+2) + element.asText
|
217
|
+
end
|
218
|
+
|
219
|
+
ret.join("\n")
|
220
|
+
end
|
221
|
+
|
222
|
+
def identifier_string
|
223
|
+
if @conditions.size == 1
|
224
|
+
how, what = @conditions.to_a.first
|
225
|
+
"#{how.inspect} and #{what.inspect}"
|
226
|
+
else
|
227
|
+
@conditions.inspect
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def selector_to_attribute(meth)
|
232
|
+
case meth
|
233
|
+
when :class_name then :class
|
234
|
+
when :caption then :value
|
235
|
+
when :url then :href
|
236
|
+
else meth
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
end # Element
|
241
|
+
end # Celerity
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Celerity
|
2
|
+
# This class is the superclass for the iterator classes (Buttons, Links, Spans etc.)
|
3
|
+
# It would normally only be accessed by the iterator methods (Browser#spans, Browser#links, ...).
|
4
|
+
class ElementCollections
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
# @api private
|
8
|
+
def initialize(container, how = nil, what = nil)
|
9
|
+
@container = container
|
10
|
+
@object = (how == :object ? what : nil)
|
11
|
+
@length = length
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [Fixnum] The number of elements in this collection.
|
15
|
+
def length
|
16
|
+
if @object
|
17
|
+
@object.length
|
18
|
+
else
|
19
|
+
@elements ||= ElementLocator.new(@container, element_class).elements_by_idents
|
20
|
+
@elements.size
|
21
|
+
end
|
22
|
+
end
|
23
|
+
alias_method :size, :length
|
24
|
+
|
25
|
+
# @yieldparam [Celerity::Element] element Iterate through the elements in this collection.
|
26
|
+
#
|
27
|
+
def each
|
28
|
+
if @elements
|
29
|
+
@elements.each { |e| yield(element_class.new(@container, :object, e)) }
|
30
|
+
else
|
31
|
+
0.upto(@length - 1) { |i| yield iterator_object(i) }
|
32
|
+
end
|
33
|
+
|
34
|
+
@length
|
35
|
+
end
|
36
|
+
|
37
|
+
# Get the element at the given index.
|
38
|
+
# This is 1-indexed to keep compatibility with Watir - subject to change.
|
39
|
+
# Also note that because of Watir's lazy loading, this will return an Element
|
40
|
+
# instance even if the index is out of bounds.
|
41
|
+
#
|
42
|
+
# @param [Fixnum] n Index of wanted element, 1-indexed.
|
43
|
+
# @return [Celerity::Element] Returns a subclass of Celerity::Element
|
44
|
+
def [](n)
|
45
|
+
if @elements && @elements[n - INDEX_OFFSET]
|
46
|
+
element_class.new(@container, :object, @elements[n - INDEX_OFFSET])
|
47
|
+
else
|
48
|
+
iterator_object(n - INDEX_OFFSET)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Note: This can be quite useful in irb:
|
53
|
+
#
|
54
|
+
# puts browser.text_fields
|
55
|
+
#
|
56
|
+
# @return [String] A string representation of all elements in this collection.
|
57
|
+
def to_s
|
58
|
+
map { |e| e.to_s }.join("\n")
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def iterator_object(i)
|
64
|
+
element_class.new(@container, :index, i+1)
|
65
|
+
end
|
66
|
+
|
67
|
+
end # ElementCollections
|
68
|
+
end # Celerity
|
@@ -0,0 +1,167 @@
|
|
1
|
+
module Celerity
|
2
|
+
|
3
|
+
# Used internally to locate elements on the page.
|
4
|
+
class ElementLocator
|
5
|
+
include Celerity::Exception
|
6
|
+
attr_accessor :idents
|
7
|
+
|
8
|
+
|
9
|
+
def initialize(container, element_class)
|
10
|
+
container.assert_exists
|
11
|
+
|
12
|
+
@container = container
|
13
|
+
@object = container.object
|
14
|
+
@element_class = element_class
|
15
|
+
@attributes = @element_class::ATTRIBUTES # could check for 'strict' here?
|
16
|
+
@idents = @element_class::TAGS
|
17
|
+
@tags = @idents.map { |e| e.tag.downcase }
|
18
|
+
end
|
19
|
+
|
20
|
+
def find_by_conditions(conditions) # TODO: refactor without performance hit
|
21
|
+
return nil unless @object # probably means we're on a TextPage (content-type is "text/plain")
|
22
|
+
|
23
|
+
@condition_idents = []
|
24
|
+
attributes = Hash.new { |h, k| h[k] = [] }
|
25
|
+
index = 0 # by default, return the first matching element
|
26
|
+
text = nil
|
27
|
+
|
28
|
+
conditions.each do |how, what|
|
29
|
+
case how
|
30
|
+
when :object
|
31
|
+
unless what.is_a?(HtmlUnit::Html::HtmlElement) || what.nil?
|
32
|
+
raise ArgumentError, "expected an HtmlUnit::Html::HtmlElement subclass, got #{what.inspect}:#{what.class}"
|
33
|
+
end
|
34
|
+
return what
|
35
|
+
when :id
|
36
|
+
return find_by_id(what)
|
37
|
+
when :xpath
|
38
|
+
return find_by_xpath(what)
|
39
|
+
when :label
|
40
|
+
return find_by_label(what)
|
41
|
+
when :class_name
|
42
|
+
how = :class
|
43
|
+
when :url
|
44
|
+
how = :href
|
45
|
+
when :caption
|
46
|
+
how = :text
|
47
|
+
end
|
48
|
+
|
49
|
+
if @attributes.include?(how = how.to_sym)
|
50
|
+
attributes[how] << what
|
51
|
+
elsif how == :index
|
52
|
+
index = what.to_i - INDEX_OFFSET
|
53
|
+
elsif how == :text
|
54
|
+
text = what
|
55
|
+
else
|
56
|
+
raise MissingWayOfFindingObjectException, "No how #{how.inspect}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
@idents.each do |ident|
|
61
|
+
merged = attributes.merge(ident.attributes) { |key, v1, v2| v1 | v2 }
|
62
|
+
id = Identifier.new(ident.tag, merged)
|
63
|
+
id.text = ident.text || text # «original» identifier takes precedence for :text
|
64
|
+
@condition_idents << id
|
65
|
+
end
|
66
|
+
|
67
|
+
if index == 0
|
68
|
+
element_by_idents(@condition_idents)
|
69
|
+
else
|
70
|
+
elements_by_idents(@condition_idents)[index]
|
71
|
+
end
|
72
|
+
|
73
|
+
rescue HtmlUnit::ElementNotFoundException
|
74
|
+
nil # for rcov
|
75
|
+
end
|
76
|
+
|
77
|
+
def find_by_id(what)
|
78
|
+
case what
|
79
|
+
when Regexp
|
80
|
+
elements_by_tag_names.find { |elem| elem.getIdAttribute =~ what }
|
81
|
+
when String
|
82
|
+
obj = @object.getHtmlElementById(what)
|
83
|
+
return obj if @tags.include?(obj.getTagName)
|
84
|
+
|
85
|
+
$stderr.puts "warning: multiple elements with identical id? (#{what.inspect})" if $VERBOSE
|
86
|
+
elements_by_tag_names.find { |elem| elem.getIdAttribute == what }
|
87
|
+
else
|
88
|
+
raise TypeError, "expected String or Regexp, got #{what.inspect}:#{what.class}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def find_by_xpath(what)
|
93
|
+
what = ".#{what}" if what[0].chr == "/"
|
94
|
+
@object.getByXPath(what).to_a.first
|
95
|
+
end
|
96
|
+
|
97
|
+
def find_by_label(what)
|
98
|
+
obj = elements_by_tag_names(%w[label]).find { |e| matches?(e.asText, what) }
|
99
|
+
|
100
|
+
return nil unless obj && (ref = obj.getReferencedElement)
|
101
|
+
return ref if @tags.include?(ref.getTagName)
|
102
|
+
|
103
|
+
find_by_id obj.getForAttribute
|
104
|
+
end
|
105
|
+
|
106
|
+
def elements_by_idents(idents = nil)
|
107
|
+
get_by_idents(:select, idents || @idents)
|
108
|
+
end
|
109
|
+
|
110
|
+
def element_by_idents(idents = nil)
|
111
|
+
get_by_idents(:find, idents || @idents)
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def get_by_idents(meth, idents)
|
117
|
+
with_nullpointer_retry do
|
118
|
+
@object.getAllHtmlChildElements.send(meth) do |e|
|
119
|
+
next unless @tags.include?(e.getTagName)
|
120
|
+
idents.any? { |id| element_matches_ident?(e, id) }
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def element_matches_ident?(element, ident)
|
126
|
+
return false unless ident.tag == element.getTagName
|
127
|
+
|
128
|
+
attr_result = ident.attributes.all? do |key, values|
|
129
|
+
values.any? { |val| matches?(element.getAttributeValue(key.to_s), val) }
|
130
|
+
end
|
131
|
+
|
132
|
+
if ident.text
|
133
|
+
attr_result && matches?(element.asText.strip, ident.text)
|
134
|
+
else
|
135
|
+
attr_result
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def elements_by_tag_names(tags = @tags)
|
140
|
+
with_nullpointer_retry do
|
141
|
+
# HtmlUnit's getHtmlElementsByTagNames won't get elements in the correct
|
142
|
+
# order (making :index fail), so we're using getAllHtmlChildElements instead.
|
143
|
+
@object.getAllHtmlChildElements.select do |elem|
|
144
|
+
tags.include?(elem.getTagName)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# HtmlUnit throws NPEs sometimes when we're locating elements
|
150
|
+
# Retry seems to work fine.
|
151
|
+
def with_nullpointer_retry(max_retries = 3)
|
152
|
+
tries = 0
|
153
|
+
yield
|
154
|
+
rescue java.lang.NullPointerException => e
|
155
|
+
raise e if tries >= max_retries
|
156
|
+
|
157
|
+
tries += 1
|
158
|
+
$stderr.puts "warning: celerity caught #{e} - retry ##{tries}"
|
159
|
+
retry
|
160
|
+
end
|
161
|
+
|
162
|
+
def matches?(string, what)
|
163
|
+
Regexp === what ? string.strip =~ what : string == what.to_s
|
164
|
+
end
|
165
|
+
|
166
|
+
end # ElementLocator
|
167
|
+
end # Celerity
|