celerity 0.0.6 → 0.0.7
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/History.txt +36 -0
- data/Manifest.txt +82 -0
- data/README.rdoc +78 -0
- data/Rakefile +25 -10
- data/celerity.gemspec +40 -0
- data/lib/celerity.rb +36 -20
- data/lib/celerity/browser.rb +396 -189
- data/lib/celerity/clickable_element.rb +25 -5
- data/lib/celerity/collections.rb +2 -2
- data/lib/celerity/container.rb +74 -61
- data/lib/celerity/default_viewer.rb +8 -4
- data/lib/celerity/disabled_element.rb +5 -5
- data/lib/celerity/element.rb +57 -35
- data/lib/celerity/element_collection.rb +22 -21
- data/lib/celerity/element_locator.rb +25 -18
- data/lib/celerity/elements/button.rb +13 -11
- data/lib/celerity/elements/file_field.rb +8 -4
- data/lib/celerity/elements/form.rb +7 -5
- data/lib/celerity/elements/frame.rb +6 -4
- data/lib/celerity/elements/image.rb +4 -4
- data/lib/celerity/elements/label.rb +1 -1
- data/lib/celerity/elements/link.rb +5 -5
- data/lib/celerity/elements/meta.rb +2 -1
- data/lib/celerity/elements/non_control_elements.rb +3 -3
- data/lib/celerity/elements/option.rb +7 -7
- data/lib/celerity/elements/radio_check.rb +18 -18
- data/lib/celerity/elements/select_list.rb +55 -25
- data/lib/celerity/elements/table.rb +21 -18
- data/lib/celerity/elements/table_cell.rb +1 -1
- data/lib/celerity/elements/table_elements.rb +1 -1
- data/lib/celerity/elements/table_row.rb +5 -5
- data/lib/celerity/elements/text_field.rb +33 -28
- data/lib/celerity/exception.rb +11 -5
- data/lib/celerity/htmlunit.rb +24 -8
- data/lib/celerity/htmlunit/commons-codec-1.4.jar +0 -0
- data/lib/celerity/htmlunit/htmlunit-2.6.jar +0 -0
- data/lib/celerity/htmlunit/htmlunit-core-js-2.6.jar +0 -0
- data/lib/celerity/htmlunit/nekohtml-1.9.13.jar +0 -0
- data/lib/celerity/htmlunit/{xercesImpl-2.8.1.jar → xercesImpl-2.9.1.jar} +0 -0
- data/lib/celerity/ignoring_web_connection.rb +15 -0
- data/lib/celerity/input_element.rb +1 -1
- data/lib/celerity/listener.rb +23 -17
- data/lib/celerity/short_inspect.rb +20 -0
- data/lib/celerity/util.rb +5 -2
- data/lib/celerity/version.rb +3 -10
- data/lib/celerity/viewer_connection.rb +89 -0
- data/lib/celerity/watir_compatibility.rb +2 -5
- data/lib/celerity/xpath_support.rb +48 -0
- data/spec/browser_authentication_spec.rb +16 -0
- data/spec/browser_spec.rb +300 -0
- data/spec/clickable_element_spec.rb +39 -0
- data/spec/default_viewer_spec.rb +23 -0
- data/spec/element_spec.rb +51 -0
- data/spec/filefield_spec.rb +18 -0
- data/spec/htmlunit_spec.rb +56 -0
- data/spec/index_offset_spec.rb +24 -0
- data/spec/listener_spec.rb +142 -0
- data/spec/spec_helper.rb +8 -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 +4 -6
- data/tasks/rspec.rake +43 -0
- data/tasks/simple_ci.rake +94 -0
- data/tasks/snapshot.rake +22 -0
- data/tasks/website.rake +17 -0
- data/tasks/yard.rake +9 -0
- metadata +59 -26
- data/README.txt +0 -69
- data/lib/celerity/extra/method_generator.rb +0 -170
- data/lib/celerity/htmlunit/commons-codec-1.3.jar +0 -0
- data/lib/celerity/htmlunit/htmlunit-2.5-SNAPSHOT.jar +0 -0
- data/lib/celerity/htmlunit/htmlunit-core-js-2.5-SNAPSHOT.jar +0 -0
- data/lib/celerity/htmlunit/nekohtml-1.9.12-20090308.130127-11.jar +0 -0
data/lib/celerity/element.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
module Celerity
|
2
|
-
|
2
|
+
|
3
3
|
#
|
4
4
|
# Superclass for all HTML elements.
|
5
|
-
#
|
6
|
-
|
5
|
+
#
|
6
|
+
|
7
7
|
class Element
|
8
8
|
include Exception
|
9
9
|
include Container
|
10
10
|
|
11
|
-
attr_reader :container
|
11
|
+
attr_reader :container
|
12
12
|
|
13
13
|
# number of spaces that separate the property from the value in the create_string method
|
14
14
|
TO_S_SIZE = 14
|
@@ -52,12 +52,13 @@ module Celerity
|
|
52
52
|
end
|
53
53
|
|
54
54
|
@conditions.freeze
|
55
|
+
@object = nil
|
55
56
|
end
|
56
57
|
|
57
58
|
#
|
58
59
|
# Get the parent element
|
59
60
|
# @return [Celerity::Element, nil] subclass of Celerity::Element, or nil if no parent was found
|
60
|
-
#
|
61
|
+
#
|
61
62
|
|
62
63
|
def parent
|
63
64
|
assert_exists
|
@@ -73,22 +74,50 @@ module Celerity
|
|
73
74
|
|
74
75
|
#
|
75
76
|
# Sets the focus to this element.
|
76
|
-
#
|
77
|
+
#
|
77
78
|
|
78
79
|
def focus
|
79
80
|
assert_exists
|
80
81
|
@object.focus
|
81
82
|
end
|
82
83
|
|
84
|
+
#
|
85
|
+
# Fires the given event for this element
|
86
|
+
#
|
87
|
+
|
88
|
+
def fire_event(event_name)
|
89
|
+
assert_exists
|
90
|
+
@object.fireEvent(event_name.sub(/^on/, ''))
|
91
|
+
end
|
92
|
+
|
83
93
|
#
|
84
94
|
# Used internally. Find the element on the page.
|
85
95
|
# @api private
|
86
|
-
#
|
96
|
+
#
|
87
97
|
|
88
98
|
def locate
|
89
99
|
@object = ElementLocator.new(@container, self.class).find_by_conditions(@conditions)
|
90
100
|
end
|
91
101
|
|
102
|
+
#
|
103
|
+
# Returns the HtmlUnit object backing this element
|
104
|
+
#
|
105
|
+
|
106
|
+
def object
|
107
|
+
@object || locate
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
# Returns a JavaScript object representing the receiver
|
112
|
+
#
|
113
|
+
# @api internal - subject to change
|
114
|
+
#
|
115
|
+
|
116
|
+
def javascript_object
|
117
|
+
assert_exists
|
118
|
+
@object.getScriptObject
|
119
|
+
end
|
120
|
+
|
92
121
|
#
|
93
122
|
# @return [String] A string representation of the element.
|
94
123
|
#
|
@@ -101,7 +130,7 @@ module Celerity
|
|
101
130
|
#
|
102
131
|
# @param [String, #to_s] The attribute.
|
103
132
|
# @return [String] The value of the given attribute.
|
104
|
-
#
|
133
|
+
#
|
105
134
|
|
106
135
|
def attribute_value(attribute)
|
107
136
|
assert_exists
|
@@ -114,17 +143,11 @@ module Celerity
|
|
114
143
|
# its parents) into account - styles from applied CSS is not considered.
|
115
144
|
#
|
116
145
|
# @return [boolean]
|
117
|
-
#
|
146
|
+
#
|
118
147
|
|
119
148
|
def visible?
|
120
|
-
|
121
|
-
|
122
|
-
return false if obj.respond_to?(:type) && obj.type == 'hidden'
|
123
|
-
return false if obj.style =~ /display\s*:\s*none|visibility\s*:\s*hidden/
|
124
|
-
obj = obj.parent
|
125
|
-
end
|
126
|
-
|
127
|
-
return true
|
149
|
+
assert_exists
|
150
|
+
@object.isDisplayed
|
128
151
|
end
|
129
152
|
|
130
153
|
#
|
@@ -132,19 +155,19 @@ module Celerity
|
|
132
155
|
#
|
133
156
|
# @raise [Celerity::Exception::UnknownObjectException] if the element can't be found.
|
134
157
|
# @api private
|
135
|
-
#
|
158
|
+
#
|
136
159
|
|
137
160
|
def assert_exists
|
138
|
-
locate
|
161
|
+
locate unless @object
|
139
162
|
unless @object
|
140
163
|
raise UnknownObjectException, "Unable to locate #{self.class.name[/::(.*)$/, 1]}, using #{identifier_string}"
|
141
164
|
end
|
142
165
|
end
|
143
|
-
|
166
|
+
|
144
167
|
#
|
145
168
|
# Checks if the element exists.
|
146
169
|
# @return [true, false]
|
147
|
-
#
|
170
|
+
#
|
148
171
|
|
149
172
|
def exists?
|
150
173
|
assert_exists
|
@@ -153,13 +176,13 @@ module Celerity
|
|
153
176
|
false
|
154
177
|
end
|
155
178
|
alias_method :exist?, :exists?
|
156
|
-
|
179
|
+
|
157
180
|
#
|
158
181
|
# Return a text representation of the element as it would appear in a browser.
|
159
182
|
#
|
160
183
|
# @see inner_text
|
161
184
|
# @return [String]
|
162
|
-
#
|
185
|
+
#
|
163
186
|
|
164
187
|
def text
|
165
188
|
assert_exists
|
@@ -173,7 +196,7 @@ module Celerity
|
|
173
196
|
#
|
174
197
|
# @see text
|
175
198
|
# @return [String]
|
176
|
-
#
|
199
|
+
#
|
177
200
|
|
178
201
|
def inner_text
|
179
202
|
assert_exists
|
@@ -182,7 +205,7 @@ module Celerity
|
|
182
205
|
|
183
206
|
#
|
184
207
|
# @return [String] The normative XML representation of the element (including children).
|
185
|
-
#
|
208
|
+
#
|
186
209
|
|
187
210
|
def to_xml
|
188
211
|
assert_exists
|
@@ -194,14 +217,14 @@ module Celerity
|
|
194
217
|
|
195
218
|
#
|
196
219
|
# @return [String] A string representation of the element's attributes.
|
197
|
-
#
|
220
|
+
#
|
198
221
|
|
199
222
|
def attribute_string
|
200
223
|
assert_exists
|
201
224
|
|
202
225
|
result = ''
|
203
226
|
@object.getAttributes.each do |attribute|
|
204
|
-
result << %Q{#{attribute.getName}="#{attribute.
|
227
|
+
result << %Q{#{attribute.getName}="#{attribute.getValue}"}
|
205
228
|
end
|
206
229
|
|
207
230
|
result
|
@@ -209,7 +232,7 @@ module Celerity
|
|
209
232
|
|
210
233
|
#
|
211
234
|
# return the canonical xpath for this element (Celerity-specific)
|
212
|
-
#
|
235
|
+
#
|
213
236
|
|
214
237
|
def xpath
|
215
238
|
assert_exists
|
@@ -223,23 +246,22 @@ module Celerity
|
|
223
246
|
#
|
224
247
|
# @return [String] The resulting attribute.
|
225
248
|
# @raise [NoMethodError] if the element doesn't support this attribute.
|
226
|
-
#
|
249
|
+
#
|
227
250
|
|
228
251
|
def method_missing(meth, *args, &blk)
|
229
252
|
assert_exists
|
230
253
|
|
231
254
|
meth = selector_to_attribute(meth)
|
232
255
|
|
233
|
-
if self.class::ATTRIBUTES.include?(meth)
|
234
|
-
return @object.
|
256
|
+
if self.class::ATTRIBUTES.include?(meth) || (self.class == Element && @object.hasAttribute(meth.to_s))
|
257
|
+
return @object.getAttribute(meth.to_s)
|
235
258
|
end
|
236
|
-
|
237
259
|
Log.warn "Element\#method_missing calling super with #{meth.inspect}"
|
238
260
|
super
|
239
261
|
end
|
240
|
-
|
262
|
+
|
241
263
|
def methods(*args)
|
242
|
-
ms = super
|
264
|
+
ms = super
|
243
265
|
ms += self.class::ATTRIBUTES.map { |e| e.to_s }
|
244
266
|
ms.sort
|
245
267
|
end
|
@@ -260,7 +282,7 @@ module Celerity
|
|
260
282
|
end
|
261
283
|
|
262
284
|
element.getAttributes.each do |attribute|
|
263
|
-
ret << " #{attribute.getName}:".ljust(TO_S_SIZE+2) + attribute.
|
285
|
+
ret << " #{attribute.getName}:".ljust(TO_S_SIZE+2) + attribute.getValue.to_s
|
264
286
|
end
|
265
287
|
|
266
288
|
unless (text = element.asText).empty?
|
@@ -1,13 +1,13 @@
|
|
1
1
|
module Celerity
|
2
|
-
|
2
|
+
|
3
3
|
#
|
4
4
|
# This class is the superclass for the iterator classes (Buttons, Links, Spans etc.)
|
5
5
|
# It would normally only be accessed by the iterator methods (Browser#spans, Browser#links, ...).
|
6
|
-
#
|
7
|
-
|
6
|
+
#
|
7
|
+
|
8
8
|
class ElementCollection
|
9
9
|
include Enumerable
|
10
|
-
|
10
|
+
|
11
11
|
#
|
12
12
|
# @api private
|
13
13
|
#
|
@@ -17,10 +17,10 @@ module Celerity
|
|
17
17
|
@object = (how == :object ? what : nil)
|
18
18
|
@length = length
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
#
|
22
22
|
# @return [Fixnum] The number of elements in this collection.
|
23
|
-
#
|
23
|
+
#
|
24
24
|
|
25
25
|
def length
|
26
26
|
if @object
|
@@ -45,22 +45,23 @@ module Celerity
|
|
45
45
|
|
46
46
|
@length
|
47
47
|
end
|
48
|
-
|
48
|
+
|
49
49
|
#
|
50
50
|
# Get the element at the given index.
|
51
|
-
#
|
51
|
+
# By default, this is 1-indexed to keep compatibility with Watir.
|
52
|
+
#
|
52
53
|
# Also note that because of Watir's lazy loading, this will return an Element
|
53
54
|
# instance even if the index is out of bounds.
|
54
55
|
#
|
55
|
-
# @param [Fixnum] n Index of wanted element, 1-indexed.
|
56
|
+
# @param [Fixnum] n Index of wanted element, 1-indexed unless Celerity.index_offset is changed.
|
56
57
|
# @return [Celerity::Element] Returns a subclass of Celerity::Element
|
57
|
-
#
|
58
|
+
#
|
58
59
|
|
59
60
|
def [](n)
|
60
|
-
if @elements && @elements[n -
|
61
|
-
element_class.new(@container, :object, @elements[n -
|
61
|
+
if @elements && @elements[n - Celerity.index_offset]
|
62
|
+
element_class.new(@container, :object, @elements[n - Celerity.index_offset])
|
62
63
|
else
|
63
|
-
iterator_object(n -
|
64
|
+
iterator_object(n - Celerity.index_offset)
|
64
65
|
end
|
65
66
|
end
|
66
67
|
|
@@ -68,20 +69,20 @@ module Celerity
|
|
68
69
|
# Get the first element in this collection. (Celerity-specific)
|
69
70
|
#
|
70
71
|
# @return [Celerity::Element] Returns a subclass of Celerity::Element
|
71
|
-
#
|
72
|
-
|
72
|
+
#
|
73
|
+
|
73
74
|
def first
|
74
|
-
self[
|
75
|
+
self[Celerity.index_offset]
|
75
76
|
end
|
76
77
|
|
77
78
|
#
|
78
79
|
# Get the last element in this collection. (Celerity-specific)
|
79
80
|
#
|
80
81
|
# @return [Celerity::Element] Returns a subclass of Celerity::Element
|
81
|
-
#
|
82
|
-
|
82
|
+
#
|
83
|
+
|
83
84
|
def last
|
84
|
-
self[
|
85
|
+
self[Celerity.index_offset - 1]
|
85
86
|
end
|
86
87
|
|
87
88
|
#
|
@@ -90,8 +91,8 @@ module Celerity
|
|
90
91
|
# puts browser.text_fields
|
91
92
|
#
|
92
93
|
# @return [String] A string representation of all elements in this collection.
|
93
|
-
#
|
94
|
-
|
94
|
+
#
|
95
|
+
|
95
96
|
def to_s
|
96
97
|
map { |e| e.to_s }.join("\n")
|
97
98
|
end
|
@@ -2,8 +2,8 @@ module Celerity
|
|
2
2
|
|
3
3
|
#
|
4
4
|
# Used internally to locate elements on the page.
|
5
|
-
#
|
6
|
-
|
5
|
+
#
|
6
|
+
|
7
7
|
class ElementLocator
|
8
8
|
include Celerity::Exception
|
9
9
|
attr_accessor :idents
|
@@ -35,8 +35,6 @@ module Celerity
|
|
35
35
|
raise ArgumentError, "expected an HtmlUnit::Html::HtmlElement subclass, got #{what.inspect}:#{what.class}"
|
36
36
|
end
|
37
37
|
return what
|
38
|
-
when :id
|
39
|
-
return find_by_id(what)
|
40
38
|
when :xpath
|
41
39
|
return find_by_xpath(what)
|
42
40
|
when :label
|
@@ -49,10 +47,12 @@ module Celerity
|
|
49
47
|
how = :text
|
50
48
|
end
|
51
49
|
|
52
|
-
if
|
50
|
+
if how == :id && conditions.size == 1
|
51
|
+
return find_by_id(what)
|
52
|
+
elsif @attributes.include?(how = how.to_sym)
|
53
53
|
attributes[how] << what
|
54
54
|
elsif how == :index
|
55
|
-
index = what.to_i -
|
55
|
+
index = what.to_i - Celerity.index_offset
|
56
56
|
elsif how == :text
|
57
57
|
text = what
|
58
58
|
else
|
@@ -82,7 +82,7 @@ module Celerity
|
|
82
82
|
when Regexp
|
83
83
|
elements_by_tag_names.find { |elem| elem.getId =~ what }
|
84
84
|
when String
|
85
|
-
obj = @object.
|
85
|
+
obj = @object.getElementById(what)
|
86
86
|
return obj if @tags.include?(obj.getTagName)
|
87
87
|
|
88
88
|
$stderr.puts "warning: multiple elements with identical id? (#{what.inspect})" if $VERBOSE
|
@@ -106,19 +106,19 @@ module Celerity
|
|
106
106
|
find_by_id obj.getForAttribute
|
107
107
|
end
|
108
108
|
|
109
|
-
def elements_by_idents(idents =
|
110
|
-
get_by_idents(:select, idents
|
109
|
+
def elements_by_idents(idents = @idents)
|
110
|
+
get_by_idents(:select, idents)
|
111
111
|
end
|
112
112
|
|
113
|
-
def element_by_idents(idents =
|
114
|
-
get_by_idents(:find, idents
|
113
|
+
def element_by_idents(idents = @idents)
|
114
|
+
get_by_idents(:find, idents)
|
115
115
|
end
|
116
116
|
|
117
117
|
private
|
118
118
|
|
119
119
|
def get_by_idents(meth, idents)
|
120
120
|
with_nullpointer_retry do
|
121
|
-
|
121
|
+
all_elements.send(meth) do |e|
|
122
122
|
next unless @tags.include?(e.getTagName)
|
123
123
|
idents.any? { |id| element_matches_ident?(e, id) }
|
124
124
|
end
|
@@ -129,7 +129,7 @@ module Celerity
|
|
129
129
|
return false unless ident.tag == element.getTagName
|
130
130
|
|
131
131
|
attr_result = ident.attributes.all? do |key, values|
|
132
|
-
values.any? { |val| matches?(element.
|
132
|
+
values.any? { |val| matches?(element.getAttribute(key.to_s), val) }
|
133
133
|
end
|
134
134
|
|
135
135
|
if ident.text
|
@@ -142,13 +142,20 @@ module Celerity
|
|
142
142
|
def elements_by_tag_names(tags = @tags)
|
143
143
|
with_nullpointer_retry do
|
144
144
|
# HtmlUnit's getHtmlElementsByTagNames won't get elements in the correct
|
145
|
-
# order (making :index fail), so we're
|
146
|
-
|
147
|
-
tags.include?(elem.getTagName)
|
148
|
-
end
|
145
|
+
# order (making :index fail), so we're looping through all elements instead.
|
146
|
+
all_elements.select { |elem| tags.include?(elem.getTagName) }
|
149
147
|
end
|
150
148
|
end
|
151
149
|
|
150
|
+
def all_elements
|
151
|
+
unless @object
|
152
|
+
raise %{internal error in #{self.class}: @container=#{@container.inspect} @element_class=#{@element_class.inspect}
|
153
|
+
Please report this failure and the code/HTML that caused it at http://github.com/jarib/celerity/issues}
|
154
|
+
end
|
155
|
+
|
156
|
+
@object.getAllHtmlChildElements
|
157
|
+
end
|
158
|
+
|
152
159
|
# HtmlUnit throws NPEs sometimes when we're locating elements
|
153
160
|
# Retry seems to work fine.
|
154
161
|
def with_nullpointer_retry(max_retries = 3)
|
@@ -158,7 +165,7 @@ module Celerity
|
|
158
165
|
raise e if tries >= max_retries
|
159
166
|
|
160
167
|
tries += 1
|
161
|
-
|
168
|
+
warn "warning: celerity caught #{e} - retry ##{tries}"
|
162
169
|
retry
|
163
170
|
end
|
164
171
|
|
@@ -1,36 +1,38 @@
|
|
1
1
|
module Celerity
|
2
|
-
|
2
|
+
|
3
3
|
#
|
4
4
|
# Input: Button
|
5
5
|
#
|
6
|
-
# Class representing button elements
|
6
|
+
# Class representing button elements.
|
7
|
+
#
|
8
|
+
# This class covers both <button> and <input type="submit|reset|image|button" /> elements.
|
7
9
|
#
|
8
|
-
|
10
|
+
|
9
11
|
class Button < InputElement
|
10
12
|
TAGS = [ Identifier.new('button'),
|
11
13
|
Identifier.new('input', :type => %w[submit reset image button]) ]
|
12
|
-
|
14
|
+
|
13
15
|
# Attribute list is a little weird due to this class covering both <button>
|
14
16
|
# and <input type="submit|reset|image|button" />
|
15
|
-
ATTRIBUTES =
|
17
|
+
ATTRIBUTES = ATTRIBUTES | [:type, :disabled, :tabindex, :accesskey, :onfocus, :onblur] | [:src, :usemap, :ismap]
|
16
18
|
DEFAULT_HOW = :value
|
17
19
|
|
18
20
|
#
|
19
21
|
# @api private
|
20
22
|
#
|
21
|
-
|
23
|
+
|
22
24
|
def locate
|
23
|
-
# We want the :value attribute to point to the inner HTML for <button> elements,
|
24
|
-
# and to the value attribute for <input type="button"> elements.
|
25
|
-
|
25
|
+
# We want the :value attribute to point to the inner HTML for <button> elements,
|
26
|
+
# and to the value attribute for <input type="button"> elements.
|
27
|
+
|
26
28
|
if (val = @conditions[:value])
|
27
29
|
button_ident = Identifier.new('button')
|
28
30
|
button_ident.text = val
|
29
31
|
input_ident = Identifier.new('input', :type => %w[submit reset image button], :value => [val])
|
30
|
-
|
32
|
+
|
31
33
|
locator = ElementLocator.new(@container, self.class)
|
32
34
|
locator.idents = [button_ident, input_ident]
|
33
|
-
|
35
|
+
|
34
36
|
conditions = @conditions.dup
|
35
37
|
conditions.delete(:value)
|
36
38
|
@object = locator.find_by_conditions(conditions)
|